merge: wave 2 executor (01-02) into main
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a5b8fcf9d3
commit
aa1a78d7d9
1081 files changed
+445341
No files matched your search
@@ -0,0 +1,22 @@
|
||||
const AGENT_URL_KEY_DELIM_RE = /[^a-z0-9]+/g;
|
||||
const AGENT_URL_KEY_TRIM_RE = /^-+|-+$/g;
|
||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
|
||||
export function isUuidLike(value: string | null | undefined): boolean {
|
||||
if (typeof value !== "string") return false;
|
||||
return UUID_RE.test(value.trim());
|
||||
}
|
||||
|
||||
export function normalizeAgentUrlKey(value: string | null | undefined): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
const normalized = value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(AGENT_URL_KEY_DELIM_RE, "-")
|
||||
.replace(AGENT_URL_KEY_TRIM_RE, "");
|
||||
return normalized.length > 0 ? normalized : null;
|
||||
}
|
||||
|
||||
export function deriveAgentUrlKey(name: string | null | undefined, fallback?: string | null): string {
|
||||
return normalizeAgentUrlKey(name) ?? normalizeAgentUrlKey(fallback) ?? "agent";
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export const API_PREFIX = "/api";
|
||||
|
||||
export const API = {
|
||||
health: `${API_PREFIX}/health`,
|
||||
companies: `${API_PREFIX}/companies`,
|
||||
agents: `${API_PREFIX}/agents`,
|
||||
projects: `${API_PREFIX}/projects`,
|
||||
issues: `${API_PREFIX}/issues`,
|
||||
goals: `${API_PREFIX}/goals`,
|
||||
approvals: `${API_PREFIX}/approvals`,
|
||||
secrets: `${API_PREFIX}/secrets`,
|
||||
costs: `${API_PREFIX}/costs`,
|
||||
activity: `${API_PREFIX}/activity`,
|
||||
dashboard: `${API_PREFIX}/dashboard`,
|
||||
sidebarBadges: `${API_PREFIX}/sidebar-badges`,
|
||||
invites: `${API_PREFIX}/invites`,
|
||||
joinRequests: `${API_PREFIX}/join-requests`,
|
||||
members: `${API_PREFIX}/members`,
|
||||
admin: `${API_PREFIX}/admin`,
|
||||
} as const;
|
||||
@@ -0,0 +1,178 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AUTH_BASE_URL_MODES,
|
||||
DEPLOYMENT_EXPOSURES,
|
||||
DEPLOYMENT_MODES,
|
||||
SECRET_PROVIDERS,
|
||||
STORAGE_PROVIDERS,
|
||||
} from "./constants.js";
|
||||
|
||||
export const configMetaSchema = z.object({
|
||||
version: z.literal(1),
|
||||
updatedAt: z.string(),
|
||||
source: z.enum(["onboard", "configure", "doctor"]),
|
||||
});
|
||||
|
||||
export const llmConfigSchema = z.object({
|
||||
provider: z.enum(["claude", "openai"]),
|
||||
apiKey: z.string().optional(),
|
||||
});
|
||||
|
||||
export const databaseBackupConfigSchema = z.object({
|
||||
enabled: z.boolean().default(true),
|
||||
intervalMinutes: z.number().int().min(1).max(7 * 24 * 60).default(60),
|
||||
retentionDays: z.number().int().min(1).max(3650).default(30),
|
||||
dir: z.string().default("~/.paperclip/instances/default/data/backups"),
|
||||
});
|
||||
|
||||
export const databaseConfigSchema = z.object({
|
||||
mode: z.enum(["embedded-postgres", "postgres"]).default("embedded-postgres"),
|
||||
connectionString: z.string().optional(),
|
||||
embeddedPostgresDataDir: z.string().default("~/.paperclip/instances/default/db"),
|
||||
embeddedPostgresPort: z.number().int().min(1).max(65535).default(54329),
|
||||
backup: databaseBackupConfigSchema.default({
|
||||
enabled: true,
|
||||
intervalMinutes: 60,
|
||||
retentionDays: 30,
|
||||
dir: "~/.paperclip/instances/default/data/backups",
|
||||
}),
|
||||
});
|
||||
|
||||
export const loggingConfigSchema = z.object({
|
||||
mode: z.enum(["file", "cloud"]),
|
||||
logDir: z.string().default("~/.paperclip/instances/default/logs"),
|
||||
});
|
||||
|
||||
export const serverConfigSchema = z.object({
|
||||
deploymentMode: z.enum(DEPLOYMENT_MODES).default("local_trusted"),
|
||||
exposure: z.enum(DEPLOYMENT_EXPOSURES).default("private"),
|
||||
host: z.string().default("127.0.0.1"),
|
||||
port: z.number().int().min(1).max(65535).default(3100),
|
||||
allowedHostnames: z.array(z.string().min(1)).default([]),
|
||||
serveUi: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const authConfigSchema = z.object({
|
||||
baseUrlMode: z.enum(AUTH_BASE_URL_MODES).default("auto"),
|
||||
publicBaseUrl: z.string().url().optional(),
|
||||
disableSignUp: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const storageLocalDiskConfigSchema = z.object({
|
||||
baseDir: z.string().default("~/.paperclip/instances/default/data/storage"),
|
||||
});
|
||||
|
||||
export const storageS3ConfigSchema = z.object({
|
||||
bucket: z.string().min(1).default("paperclip"),
|
||||
region: z.string().min(1).default("us-east-1"),
|
||||
endpoint: z.string().optional(),
|
||||
prefix: z.string().default(""),
|
||||
forcePathStyle: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const storageConfigSchema = z.object({
|
||||
provider: z.enum(STORAGE_PROVIDERS).default("local_disk"),
|
||||
localDisk: storageLocalDiskConfigSchema.default({
|
||||
baseDir: "~/.paperclip/instances/default/data/storage",
|
||||
}),
|
||||
s3: storageS3ConfigSchema.default({
|
||||
bucket: "paperclip",
|
||||
region: "us-east-1",
|
||||
prefix: "",
|
||||
forcePathStyle: false,
|
||||
}),
|
||||
});
|
||||
|
||||
export const secretsLocalEncryptedConfigSchema = z.object({
|
||||
keyFilePath: z.string().default("~/.paperclip/instances/default/secrets/master.key"),
|
||||
});
|
||||
|
||||
export const secretsConfigSchema = z.object({
|
||||
provider: z.enum(SECRET_PROVIDERS).default("local_encrypted"),
|
||||
strictMode: z.boolean().default(false),
|
||||
localEncrypted: secretsLocalEncryptedConfigSchema.default({
|
||||
keyFilePath: "~/.paperclip/instances/default/secrets/master.key",
|
||||
}),
|
||||
});
|
||||
|
||||
export const paperclipConfigSchema = z
|
||||
.object({
|
||||
$meta: configMetaSchema,
|
||||
llm: llmConfigSchema.optional(),
|
||||
database: databaseConfigSchema,
|
||||
logging: loggingConfigSchema,
|
||||
server: serverConfigSchema,
|
||||
auth: authConfigSchema.default({
|
||||
baseUrlMode: "auto",
|
||||
disableSignUp: false,
|
||||
}),
|
||||
storage: storageConfigSchema.default({
|
||||
provider: "local_disk",
|
||||
localDisk: {
|
||||
baseDir: "~/.paperclip/instances/default/data/storage",
|
||||
},
|
||||
s3: {
|
||||
bucket: "paperclip",
|
||||
region: "us-east-1",
|
||||
prefix: "",
|
||||
forcePathStyle: false,
|
||||
},
|
||||
}),
|
||||
secrets: secretsConfigSchema.default({
|
||||
provider: "local_encrypted",
|
||||
strictMode: false,
|
||||
localEncrypted: {
|
||||
keyFilePath: "~/.paperclip/instances/default/secrets/master.key",
|
||||
},
|
||||
}),
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
if (value.server.deploymentMode === "local_trusted") {
|
||||
if (value.server.exposure !== "private") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "server.exposure must be private when deploymentMode is local_trusted",
|
||||
path: ["server", "exposure"],
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.auth.baseUrlMode === "explicit" && !value.auth.publicBaseUrl) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "auth.publicBaseUrl is required when auth.baseUrlMode is explicit",
|
||||
path: ["auth", "publicBaseUrl"],
|
||||
});
|
||||
}
|
||||
|
||||
if (value.server.exposure === "public" && value.auth.baseUrlMode !== "explicit") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "auth.baseUrlMode must be explicit when deploymentMode=authenticated and exposure=public",
|
||||
path: ["auth", "baseUrlMode"],
|
||||
});
|
||||
}
|
||||
|
||||
if (value.server.exposure === "public" && !value.auth.publicBaseUrl) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "auth.publicBaseUrl is required when deploymentMode=authenticated and exposure=public",
|
||||
path: ["auth", "publicBaseUrl"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type PaperclipConfig = z.infer<typeof paperclipConfigSchema>;
|
||||
export type LlmConfig = z.infer<typeof llmConfigSchema>;
|
||||
export type DatabaseConfig = z.infer<typeof databaseConfigSchema>;
|
||||
export type LoggingConfig = z.infer<typeof loggingConfigSchema>;
|
||||
export type ServerConfig = z.infer<typeof serverConfigSchema>;
|
||||
export type StorageConfig = z.infer<typeof storageConfigSchema>;
|
||||
export type StorageLocalDiskConfig = z.infer<typeof storageLocalDiskConfigSchema>;
|
||||
export type StorageS3Config = z.infer<typeof storageS3ConfigSchema>;
|
||||
export type SecretsConfig = z.infer<typeof secretsConfigSchema>;
|
||||
export type SecretsLocalEncryptedConfig = z.infer<typeof secretsLocalEncryptedConfigSchema>;
|
||||
export type AuthConfig = z.infer<typeof authConfigSchema>;
|
||||
export type ConfigMeta = z.infer<typeof configMetaSchema>;
|
||||
export type DatabaseBackupConfig = z.infer<typeof databaseBackupConfigSchema>;
|
||||
@@ -0,0 +1,689 @@
|
||||
export const COMPANY_STATUSES = ["active", "paused", "archived"] as const;
|
||||
export type CompanyStatus = (typeof COMPANY_STATUSES)[number];
|
||||
|
||||
export const DEPLOYMENT_MODES = ["local_trusted", "authenticated"] as const;
|
||||
export type DeploymentMode = (typeof DEPLOYMENT_MODES)[number];
|
||||
|
||||
export const DEPLOYMENT_EXPOSURES = ["private", "public"] as const;
|
||||
export type DeploymentExposure = (typeof DEPLOYMENT_EXPOSURES)[number];
|
||||
|
||||
export const AUTH_BASE_URL_MODES = ["auto", "explicit"] as const;
|
||||
export type AuthBaseUrlMode = (typeof AUTH_BASE_URL_MODES)[number];
|
||||
|
||||
export const AGENT_STATUSES = [
|
||||
"active",
|
||||
"paused",
|
||||
"idle",
|
||||
"running",
|
||||
"error",
|
||||
"pending_approval",
|
||||
"terminated",
|
||||
] as const;
|
||||
export type AgentStatus = (typeof AGENT_STATUSES)[number];
|
||||
|
||||
export const AGENT_ADAPTER_TYPES = [
|
||||
"process",
|
||||
"http",
|
||||
"claude_local",
|
||||
"codex_local",
|
||||
"opencode_local",
|
||||
"pi_local",
|
||||
"cursor",
|
||||
"openclaw_gateway",
|
||||
"hermes_local",
|
||||
] as const;
|
||||
export type AgentAdapterType = (typeof AGENT_ADAPTER_TYPES)[number];
|
||||
|
||||
export const AGENT_ROLES = [
|
||||
"ceo",
|
||||
"cto",
|
||||
"cmo",
|
||||
"cfo",
|
||||
"engineer",
|
||||
"designer",
|
||||
"pm",
|
||||
"qa",
|
||||
"devops",
|
||||
"researcher",
|
||||
"general",
|
||||
] as const;
|
||||
export type AgentRole = (typeof AGENT_ROLES)[number];
|
||||
|
||||
export const AGENT_ROLE_LABELS: Record<AgentRole, string> = {
|
||||
ceo: "CEO",
|
||||
cto: "CTO",
|
||||
cmo: "CMO",
|
||||
cfo: "CFO",
|
||||
engineer: "Engineer",
|
||||
designer: "Designer",
|
||||
pm: "PM",
|
||||
qa: "QA",
|
||||
devops: "DevOps",
|
||||
researcher: "Researcher",
|
||||
general: "General",
|
||||
};
|
||||
|
||||
export const AGENT_ICON_NAMES = [
|
||||
"bot",
|
||||
"cpu",
|
||||
"brain",
|
||||
"zap",
|
||||
"rocket",
|
||||
"code",
|
||||
"terminal",
|
||||
"shield",
|
||||
"eye",
|
||||
"search",
|
||||
"wrench",
|
||||
"hammer",
|
||||
"lightbulb",
|
||||
"sparkles",
|
||||
"star",
|
||||
"heart",
|
||||
"flame",
|
||||
"bug",
|
||||
"cog",
|
||||
"database",
|
||||
"globe",
|
||||
"lock",
|
||||
"mail",
|
||||
"message-square",
|
||||
"file-code",
|
||||
"git-branch",
|
||||
"package",
|
||||
"puzzle",
|
||||
"target",
|
||||
"wand",
|
||||
"atom",
|
||||
"circuit-board",
|
||||
"radar",
|
||||
"swords",
|
||||
"telescope",
|
||||
"microscope",
|
||||
"crown",
|
||||
"gem",
|
||||
"hexagon",
|
||||
"pentagon",
|
||||
"fingerprint",
|
||||
] as const;
|
||||
export type AgentIconName = (typeof AGENT_ICON_NAMES)[number];
|
||||
|
||||
export const ISSUE_STATUSES = [
|
||||
"backlog",
|
||||
"todo",
|
||||
"in_progress",
|
||||
"in_review",
|
||||
"done",
|
||||
"blocked",
|
||||
"cancelled",
|
||||
] as const;
|
||||
export type IssueStatus = (typeof ISSUE_STATUSES)[number];
|
||||
|
||||
export const ISSUE_PRIORITIES = ["critical", "high", "medium", "low"] as const;
|
||||
export type IssuePriority = (typeof ISSUE_PRIORITIES)[number];
|
||||
|
||||
export const ISSUE_ORIGIN_KINDS = ["manual", "routine_execution"] as const;
|
||||
export type IssueOriginKind = (typeof ISSUE_ORIGIN_KINDS)[number];
|
||||
|
||||
export const GOAL_LEVELS = ["company", "team", "agent", "task"] as const;
|
||||
export type GoalLevel = (typeof GOAL_LEVELS)[number];
|
||||
|
||||
export const GOAL_STATUSES = ["planned", "active", "achieved", "cancelled"] as const;
|
||||
export type GoalStatus = (typeof GOAL_STATUSES)[number];
|
||||
|
||||
export const PROJECT_STATUSES = [
|
||||
"backlog",
|
||||
"planned",
|
||||
"in_progress",
|
||||
"completed",
|
||||
"cancelled",
|
||||
] as const;
|
||||
export type ProjectStatus = (typeof PROJECT_STATUSES)[number];
|
||||
|
||||
export const ROUTINE_STATUSES = ["active", "paused", "archived"] as const;
|
||||
export type RoutineStatus = (typeof ROUTINE_STATUSES)[number];
|
||||
|
||||
export const ROUTINE_CONCURRENCY_POLICIES = ["coalesce_if_active", "always_enqueue", "skip_if_active"] as const;
|
||||
export type RoutineConcurrencyPolicy = (typeof ROUTINE_CONCURRENCY_POLICIES)[number];
|
||||
|
||||
export const ROUTINE_CATCH_UP_POLICIES = ["skip_missed", "enqueue_missed_with_cap"] as const;
|
||||
export type RoutineCatchUpPolicy = (typeof ROUTINE_CATCH_UP_POLICIES)[number];
|
||||
|
||||
export const ROUTINE_TRIGGER_KINDS = ["schedule", "webhook", "api"] as const;
|
||||
export type RoutineTriggerKind = (typeof ROUTINE_TRIGGER_KINDS)[number];
|
||||
|
||||
export const ROUTINE_TRIGGER_SIGNING_MODES = ["bearer", "hmac_sha256"] as const;
|
||||
export type RoutineTriggerSigningMode = (typeof ROUTINE_TRIGGER_SIGNING_MODES)[number];
|
||||
|
||||
export const ROUTINE_RUN_STATUSES = [
|
||||
"received",
|
||||
"coalesced",
|
||||
"skipped",
|
||||
"issue_created",
|
||||
"completed",
|
||||
"failed",
|
||||
] as const;
|
||||
export type RoutineRunStatus = (typeof ROUTINE_RUN_STATUSES)[number];
|
||||
|
||||
export const ROUTINE_RUN_SOURCES = ["schedule", "manual", "api", "webhook"] as const;
|
||||
export type RoutineRunSource = (typeof ROUTINE_RUN_SOURCES)[number];
|
||||
|
||||
export const PAUSE_REASONS = ["manual", "budget", "system"] as const;
|
||||
export type PauseReason = (typeof PAUSE_REASONS)[number];
|
||||
|
||||
export const PROJECT_COLORS = [
|
||||
"#6366f1", // indigo
|
||||
"#8b5cf6", // violet
|
||||
"#ec4899", // pink
|
||||
"#ef4444", // red
|
||||
"#f97316", // orange
|
||||
"#eab308", // yellow
|
||||
"#22c55e", // green
|
||||
"#14b8a6", // teal
|
||||
"#06b6d4", // cyan
|
||||
"#3b82f6", // blue
|
||||
] as const;
|
||||
|
||||
export const APPROVAL_TYPES = ["hire_agent", "approve_ceo_strategy", "budget_override_required"] as const;
|
||||
export type ApprovalType = (typeof APPROVAL_TYPES)[number];
|
||||
|
||||
export const APPROVAL_STATUSES = [
|
||||
"pending",
|
||||
"revision_requested",
|
||||
"approved",
|
||||
"rejected",
|
||||
"cancelled",
|
||||
] as const;
|
||||
export type ApprovalStatus = (typeof APPROVAL_STATUSES)[number];
|
||||
|
||||
export const SECRET_PROVIDERS = [
|
||||
"local_encrypted",
|
||||
"aws_secrets_manager",
|
||||
"gcp_secret_manager",
|
||||
"vault",
|
||||
] as const;
|
||||
export type SecretProvider = (typeof SECRET_PROVIDERS)[number];
|
||||
|
||||
export const STORAGE_PROVIDERS = ["local_disk", "s3"] as const;
|
||||
export type StorageProvider = (typeof STORAGE_PROVIDERS)[number];
|
||||
|
||||
export const BILLING_TYPES = [
|
||||
"metered_api",
|
||||
"subscription_included",
|
||||
"subscription_overage",
|
||||
"credits",
|
||||
"fixed",
|
||||
"unknown",
|
||||
] as const;
|
||||
export type BillingType = (typeof BILLING_TYPES)[number];
|
||||
|
||||
export const FINANCE_EVENT_KINDS = [
|
||||
"inference_charge",
|
||||
"platform_fee",
|
||||
"credit_purchase",
|
||||
"credit_refund",
|
||||
"credit_expiry",
|
||||
"byok_fee",
|
||||
"gateway_overhead",
|
||||
"log_storage_charge",
|
||||
"logpush_charge",
|
||||
"provisioned_capacity_charge",
|
||||
"training_charge",
|
||||
"custom_model_import_charge",
|
||||
"custom_model_storage_charge",
|
||||
"manual_adjustment",
|
||||
] as const;
|
||||
export type FinanceEventKind = (typeof FINANCE_EVENT_KINDS)[number];
|
||||
|
||||
export const FINANCE_DIRECTIONS = ["debit", "credit"] as const;
|
||||
export type FinanceDirection = (typeof FINANCE_DIRECTIONS)[number];
|
||||
|
||||
export const FINANCE_UNITS = [
|
||||
"input_token",
|
||||
"output_token",
|
||||
"cached_input_token",
|
||||
"request",
|
||||
"credit_usd",
|
||||
"credit_unit",
|
||||
"model_unit_minute",
|
||||
"model_unit_hour",
|
||||
"gb_month",
|
||||
"train_token",
|
||||
"unknown",
|
||||
] as const;
|
||||
export type FinanceUnit = (typeof FINANCE_UNITS)[number];
|
||||
|
||||
export const BUDGET_SCOPE_TYPES = ["company", "agent", "project"] as const;
|
||||
export type BudgetScopeType = (typeof BUDGET_SCOPE_TYPES)[number];
|
||||
|
||||
export const BUDGET_METRICS = ["billed_cents"] as const;
|
||||
export type BudgetMetric = (typeof BUDGET_METRICS)[number];
|
||||
|
||||
export const BUDGET_WINDOW_KINDS = ["calendar_month_utc", "lifetime"] as const;
|
||||
export type BudgetWindowKind = (typeof BUDGET_WINDOW_KINDS)[number];
|
||||
|
||||
export const BUDGET_THRESHOLD_TYPES = ["soft", "hard"] as const;
|
||||
export type BudgetThresholdType = (typeof BUDGET_THRESHOLD_TYPES)[number];
|
||||
|
||||
export const BUDGET_INCIDENT_STATUSES = ["open", "resolved", "dismissed"] as const;
|
||||
export type BudgetIncidentStatus = (typeof BUDGET_INCIDENT_STATUSES)[number];
|
||||
|
||||
export const BUDGET_INCIDENT_RESOLUTION_ACTIONS = [
|
||||
"keep_paused",
|
||||
"raise_budget_and_resume",
|
||||
] as const;
|
||||
export type BudgetIncidentResolutionAction = (typeof BUDGET_INCIDENT_RESOLUTION_ACTIONS)[number];
|
||||
|
||||
export const HEARTBEAT_INVOCATION_SOURCES = [
|
||||
"timer",
|
||||
"assignment",
|
||||
"on_demand",
|
||||
"automation",
|
||||
] as const;
|
||||
export type HeartbeatInvocationSource = (typeof HEARTBEAT_INVOCATION_SOURCES)[number];
|
||||
|
||||
export const WAKEUP_TRIGGER_DETAILS = ["manual", "ping", "callback", "system"] as const;
|
||||
export type WakeupTriggerDetail = (typeof WAKEUP_TRIGGER_DETAILS)[number];
|
||||
|
||||
export const WAKEUP_REQUEST_STATUSES = [
|
||||
"queued",
|
||||
"deferred_issue_execution",
|
||||
"claimed",
|
||||
"coalesced",
|
||||
"skipped",
|
||||
"completed",
|
||||
"failed",
|
||||
"cancelled",
|
||||
] as const;
|
||||
export type WakeupRequestStatus = (typeof WAKEUP_REQUEST_STATUSES)[number];
|
||||
|
||||
export const HEARTBEAT_RUN_STATUSES = [
|
||||
"queued",
|
||||
"running",
|
||||
"succeeded",
|
||||
"failed",
|
||||
"cancelled",
|
||||
"timed_out",
|
||||
] as const;
|
||||
export type HeartbeatRunStatus = (typeof HEARTBEAT_RUN_STATUSES)[number];
|
||||
|
||||
export const LIVE_EVENT_TYPES = [
|
||||
"heartbeat.run.queued",
|
||||
"heartbeat.run.status",
|
||||
"heartbeat.run.event",
|
||||
"heartbeat.run.log",
|
||||
"agent.status",
|
||||
"activity.logged",
|
||||
"plugin.ui.updated",
|
||||
"plugin.worker.crashed",
|
||||
"plugin.worker.restarted",
|
||||
] as const;
|
||||
export type LiveEventType = (typeof LIVE_EVENT_TYPES)[number];
|
||||
|
||||
export const PRINCIPAL_TYPES = ["user", "agent"] as const;
|
||||
export type PrincipalType = (typeof PRINCIPAL_TYPES)[number];
|
||||
|
||||
export const MEMBERSHIP_STATUSES = ["pending", "active", "suspended"] as const;
|
||||
export type MembershipStatus = (typeof MEMBERSHIP_STATUSES)[number];
|
||||
|
||||
export const INSTANCE_USER_ROLES = ["instance_admin"] as const;
|
||||
export type InstanceUserRole = (typeof INSTANCE_USER_ROLES)[number];
|
||||
|
||||
export const INVITE_TYPES = ["company_join", "bootstrap_ceo"] as const;
|
||||
export type InviteType = (typeof INVITE_TYPES)[number];
|
||||
|
||||
export const INVITE_JOIN_TYPES = ["human", "agent", "both"] as const;
|
||||
export type InviteJoinType = (typeof INVITE_JOIN_TYPES)[number];
|
||||
|
||||
export const JOIN_REQUEST_TYPES = ["human", "agent"] as const;
|
||||
export type JoinRequestType = (typeof JOIN_REQUEST_TYPES)[number];
|
||||
|
||||
export const JOIN_REQUEST_STATUSES = ["pending_approval", "approved", "rejected"] as const;
|
||||
export type JoinRequestStatus = (typeof JOIN_REQUEST_STATUSES)[number];
|
||||
|
||||
export const PERMISSION_KEYS = [
|
||||
"agents:create",
|
||||
"users:invite",
|
||||
"users:manage_permissions",
|
||||
"tasks:assign",
|
||||
"tasks:assign_scope",
|
||||
"joins:approve",
|
||||
] as const;
|
||||
export type PermissionKey = (typeof PERMISSION_KEYS)[number];
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin System — see doc/plugins/PLUGIN_SPEC.md for the full specification
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The current version of the Plugin API contract.
|
||||
*
|
||||
* Increment this value whenever a breaking change is made to the plugin API
|
||||
* so that the host can reject incompatible plugin manifests.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §4 — Versioning
|
||||
*/
|
||||
export const PLUGIN_API_VERSION = 1 as const;
|
||||
|
||||
/**
|
||||
* Lifecycle statuses for an installed plugin.
|
||||
*
|
||||
* State machine: installed → ready | error, ready → disabled | error | upgrade_pending | uninstalled,
|
||||
* disabled → ready | uninstalled, error → ready | uninstalled,
|
||||
* upgrade_pending → ready | error | uninstalled, uninstalled → installed (reinstall).
|
||||
*
|
||||
* @see {@link PluginStatus} — inferred union type
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugins.status`
|
||||
*/
|
||||
export const PLUGIN_STATUSES = [
|
||||
"installed",
|
||||
"ready",
|
||||
"disabled",
|
||||
"error",
|
||||
"upgrade_pending",
|
||||
"uninstalled",
|
||||
] as const;
|
||||
export type PluginStatus = (typeof PLUGIN_STATUSES)[number];
|
||||
|
||||
/**
|
||||
* Plugin classification categories. A plugin declares one or more categories
|
||||
* in its manifest to describe its primary purpose.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §6.2
|
||||
*/
|
||||
export const PLUGIN_CATEGORIES = [
|
||||
"connector",
|
||||
"workspace",
|
||||
"automation",
|
||||
"ui",
|
||||
] as const;
|
||||
export type PluginCategory = (typeof PLUGIN_CATEGORIES)[number];
|
||||
|
||||
/**
|
||||
* Named permissions the host grants to a plugin. Plugins declare required
|
||||
* capabilities in their manifest; the host enforces them at runtime via the
|
||||
* plugin capability validator.
|
||||
*
|
||||
* Grouped into: Data Read, Data Write, Plugin State, Runtime/Integration,
|
||||
* Agent Tools, and UI.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §15 — Capability Model
|
||||
*/
|
||||
export const PLUGIN_CAPABILITIES = [
|
||||
// Data Read
|
||||
"companies.read",
|
||||
"projects.read",
|
||||
"project.workspaces.read",
|
||||
"issues.read",
|
||||
"issue.comments.read",
|
||||
"issue.documents.read",
|
||||
"agents.read",
|
||||
"goals.read",
|
||||
"goals.create",
|
||||
"goals.update",
|
||||
"activity.read",
|
||||
"costs.read",
|
||||
// Data Write
|
||||
"issues.create",
|
||||
"issues.update",
|
||||
"issue.comments.create",
|
||||
"issue.documents.write",
|
||||
"agents.pause",
|
||||
"agents.resume",
|
||||
"agents.invoke",
|
||||
"agent.sessions.create",
|
||||
"agent.sessions.list",
|
||||
"agent.sessions.send",
|
||||
"agent.sessions.close",
|
||||
"activity.log.write",
|
||||
"metrics.write",
|
||||
// Plugin State
|
||||
"plugin.state.read",
|
||||
"plugin.state.write",
|
||||
// Runtime / Integration
|
||||
"events.subscribe",
|
||||
"events.emit",
|
||||
"jobs.schedule",
|
||||
"webhooks.receive",
|
||||
"http.outbound",
|
||||
"secrets.read-ref",
|
||||
// Agent Tools
|
||||
"agent.tools.register",
|
||||
// UI
|
||||
"instance.settings.register",
|
||||
"ui.sidebar.register",
|
||||
"ui.page.register",
|
||||
"ui.detailTab.register",
|
||||
"ui.dashboardWidget.register",
|
||||
"ui.commentAnnotation.register",
|
||||
"ui.action.register",
|
||||
] as const;
|
||||
export type PluginCapability = (typeof PLUGIN_CAPABILITIES)[number];
|
||||
|
||||
/**
|
||||
* UI extension slot types. Each slot type corresponds to a mount point in the
|
||||
* Paperclip UI where plugin components can be rendered.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
*/
|
||||
export const PLUGIN_UI_SLOT_TYPES = [
|
||||
"page",
|
||||
"detailTab",
|
||||
"taskDetailView",
|
||||
"dashboardWidget",
|
||||
"sidebar",
|
||||
"sidebarPanel",
|
||||
"projectSidebarItem",
|
||||
"globalToolbarButton",
|
||||
"toolbarButton",
|
||||
"contextMenuItem",
|
||||
"commentAnnotation",
|
||||
"commentContextMenuItem",
|
||||
"settingsPage",
|
||||
] as const;
|
||||
export type PluginUiSlotType = (typeof PLUGIN_UI_SLOT_TYPES)[number];
|
||||
|
||||
/**
|
||||
* Reserved company-scoped route segments that plugin page routes may not claim.
|
||||
*
|
||||
* These map to first-class host pages under `/:companyPrefix/...`.
|
||||
*/
|
||||
export const PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS = [
|
||||
"dashboard",
|
||||
"onboarding",
|
||||
"companies",
|
||||
"company",
|
||||
"settings",
|
||||
"plugins",
|
||||
"org",
|
||||
"agents",
|
||||
"projects",
|
||||
"issues",
|
||||
"goals",
|
||||
"approvals",
|
||||
"costs",
|
||||
"activity",
|
||||
"inbox",
|
||||
"design-guide",
|
||||
"tests",
|
||||
] as const;
|
||||
export type PluginReservedCompanyRouteSegment =
|
||||
(typeof PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS)[number];
|
||||
|
||||
/**
|
||||
* Launcher placement zones describe where a plugin-owned launcher can appear
|
||||
* in the host UI. These are intentionally aligned with current slot surfaces
|
||||
* so manifest authors can describe launch intent without coupling to a single
|
||||
* component implementation detail.
|
||||
*/
|
||||
export const PLUGIN_LAUNCHER_PLACEMENT_ZONES = [
|
||||
"page",
|
||||
"detailTab",
|
||||
"taskDetailView",
|
||||
"dashboardWidget",
|
||||
"sidebar",
|
||||
"sidebarPanel",
|
||||
"projectSidebarItem",
|
||||
"globalToolbarButton",
|
||||
"toolbarButton",
|
||||
"contextMenuItem",
|
||||
"commentAnnotation",
|
||||
"commentContextMenuItem",
|
||||
"settingsPage",
|
||||
] as const;
|
||||
export type PluginLauncherPlacementZone = (typeof PLUGIN_LAUNCHER_PLACEMENT_ZONES)[number];
|
||||
|
||||
/**
|
||||
* Launcher action kinds describe what the launcher does when activated.
|
||||
*/
|
||||
export const PLUGIN_LAUNCHER_ACTIONS = [
|
||||
"navigate",
|
||||
"openModal",
|
||||
"openDrawer",
|
||||
"openPopover",
|
||||
"performAction",
|
||||
"deepLink",
|
||||
] as const;
|
||||
export type PluginLauncherAction = (typeof PLUGIN_LAUNCHER_ACTIONS)[number];
|
||||
|
||||
/**
|
||||
* Optional size hints the host can use when rendering plugin-owned launcher
|
||||
* destinations such as overlays, drawers, or full page handoffs.
|
||||
*/
|
||||
export const PLUGIN_LAUNCHER_BOUNDS = [
|
||||
"inline",
|
||||
"compact",
|
||||
"default",
|
||||
"wide",
|
||||
"full",
|
||||
] as const;
|
||||
export type PluginLauncherBounds = (typeof PLUGIN_LAUNCHER_BOUNDS)[number];
|
||||
|
||||
/**
|
||||
* Render environments describe the container a launcher expects after it is
|
||||
* activated. The current host may map these to concrete UI primitives.
|
||||
*/
|
||||
export const PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS = [
|
||||
"hostInline",
|
||||
"hostOverlay",
|
||||
"hostRoute",
|
||||
"external",
|
||||
"iframe",
|
||||
] as const;
|
||||
export type PluginLauncherRenderEnvironment =
|
||||
(typeof PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS)[number];
|
||||
|
||||
/**
|
||||
* Entity types that a `detailTab` UI slot can attach to.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.3 — Detail Tabs
|
||||
*/
|
||||
export const PLUGIN_UI_SLOT_ENTITY_TYPES = [
|
||||
"project",
|
||||
"issue",
|
||||
"agent",
|
||||
"goal",
|
||||
"run",
|
||||
"comment",
|
||||
] as const;
|
||||
export type PluginUiSlotEntityType = (typeof PLUGIN_UI_SLOT_ENTITY_TYPES)[number];
|
||||
|
||||
/**
|
||||
* Scope kinds for plugin state storage. Determines the granularity at which
|
||||
* a plugin stores key-value state data.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_state.scope_kind`
|
||||
*/
|
||||
export const PLUGIN_STATE_SCOPE_KINDS = [
|
||||
"instance",
|
||||
"company",
|
||||
"project",
|
||||
"project_workspace",
|
||||
"agent",
|
||||
"issue",
|
||||
"goal",
|
||||
"run",
|
||||
] as const;
|
||||
export type PluginStateScopeKind = (typeof PLUGIN_STATE_SCOPE_KINDS)[number];
|
||||
|
||||
/** Statuses for a plugin's scheduled job definition. */
|
||||
export const PLUGIN_JOB_STATUSES = [
|
||||
"active",
|
||||
"paused",
|
||||
"failed",
|
||||
] as const;
|
||||
export type PluginJobStatus = (typeof PLUGIN_JOB_STATUSES)[number];
|
||||
|
||||
/** Statuses for individual job run executions. */
|
||||
export const PLUGIN_JOB_RUN_STATUSES = [
|
||||
"pending",
|
||||
"queued",
|
||||
"running",
|
||||
"succeeded",
|
||||
"failed",
|
||||
"cancelled",
|
||||
] as const;
|
||||
export type PluginJobRunStatus = (typeof PLUGIN_JOB_RUN_STATUSES)[number];
|
||||
|
||||
/** What triggered a particular job run. */
|
||||
export const PLUGIN_JOB_RUN_TRIGGERS = [
|
||||
"schedule",
|
||||
"manual",
|
||||
"retry",
|
||||
] as const;
|
||||
export type PluginJobRunTrigger = (typeof PLUGIN_JOB_RUN_TRIGGERS)[number];
|
||||
|
||||
/** Statuses for inbound webhook deliveries. */
|
||||
export const PLUGIN_WEBHOOK_DELIVERY_STATUSES = [
|
||||
"pending",
|
||||
"success",
|
||||
"failed",
|
||||
] as const;
|
||||
export type PluginWebhookDeliveryStatus = (typeof PLUGIN_WEBHOOK_DELIVERY_STATUSES)[number];
|
||||
|
||||
/**
|
||||
* Core domain event types that plugins can subscribe to via the
|
||||
* `events.subscribe` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §16 — Event System
|
||||
*/
|
||||
export const PLUGIN_EVENT_TYPES = [
|
||||
"company.created",
|
||||
"company.updated",
|
||||
"project.created",
|
||||
"project.updated",
|
||||
"project.workspace_created",
|
||||
"project.workspace_updated",
|
||||
"project.workspace_deleted",
|
||||
"issue.created",
|
||||
"issue.updated",
|
||||
"issue.comment.created",
|
||||
"agent.created",
|
||||
"agent.updated",
|
||||
"agent.status_changed",
|
||||
"agent.run.started",
|
||||
"agent.run.finished",
|
||||
"agent.run.failed",
|
||||
"agent.run.cancelled",
|
||||
"goal.created",
|
||||
"goal.updated",
|
||||
"approval.created",
|
||||
"approval.decided",
|
||||
"cost_event.created",
|
||||
"activity.logged",
|
||||
] as const;
|
||||
export type PluginEventType = (typeof PLUGIN_EVENT_TYPES)[number];
|
||||
|
||||
/**
|
||||
* Error codes returned by the plugin bridge when a UI → worker call fails.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export const PLUGIN_BRIDGE_ERROR_CODES = [
|
||||
"WORKER_UNAVAILABLE",
|
||||
"CAPABILITY_DENIED",
|
||||
"WORKER_ERROR",
|
||||
"TIMEOUT",
|
||||
"UNKNOWN",
|
||||
] as const;
|
||||
export type PluginBridgeErrorCode = (typeof PLUGIN_BRIDGE_ERROR_CODES)[number];
|
||||
@@ -0,0 +1,582 @@
|
||||
export {
|
||||
COMPANY_STATUSES,
|
||||
DEPLOYMENT_MODES,
|
||||
DEPLOYMENT_EXPOSURES,
|
||||
AUTH_BASE_URL_MODES,
|
||||
AGENT_STATUSES,
|
||||
AGENT_ADAPTER_TYPES,
|
||||
AGENT_ROLES,
|
||||
AGENT_ROLE_LABELS,
|
||||
AGENT_ICON_NAMES,
|
||||
ISSUE_STATUSES,
|
||||
ISSUE_PRIORITIES,
|
||||
ISSUE_ORIGIN_KINDS,
|
||||
GOAL_LEVELS,
|
||||
GOAL_STATUSES,
|
||||
PROJECT_STATUSES,
|
||||
ROUTINE_STATUSES,
|
||||
ROUTINE_CONCURRENCY_POLICIES,
|
||||
ROUTINE_CATCH_UP_POLICIES,
|
||||
ROUTINE_TRIGGER_KINDS,
|
||||
ROUTINE_TRIGGER_SIGNING_MODES,
|
||||
ROUTINE_RUN_STATUSES,
|
||||
ROUTINE_RUN_SOURCES,
|
||||
PAUSE_REASONS,
|
||||
PROJECT_COLORS,
|
||||
APPROVAL_TYPES,
|
||||
APPROVAL_STATUSES,
|
||||
SECRET_PROVIDERS,
|
||||
STORAGE_PROVIDERS,
|
||||
BILLING_TYPES,
|
||||
FINANCE_EVENT_KINDS,
|
||||
FINANCE_DIRECTIONS,
|
||||
FINANCE_UNITS,
|
||||
BUDGET_SCOPE_TYPES,
|
||||
BUDGET_METRICS,
|
||||
BUDGET_WINDOW_KINDS,
|
||||
BUDGET_THRESHOLD_TYPES,
|
||||
BUDGET_INCIDENT_STATUSES,
|
||||
BUDGET_INCIDENT_RESOLUTION_ACTIONS,
|
||||
HEARTBEAT_INVOCATION_SOURCES,
|
||||
HEARTBEAT_RUN_STATUSES,
|
||||
WAKEUP_TRIGGER_DETAILS,
|
||||
WAKEUP_REQUEST_STATUSES,
|
||||
LIVE_EVENT_TYPES,
|
||||
PRINCIPAL_TYPES,
|
||||
MEMBERSHIP_STATUSES,
|
||||
INSTANCE_USER_ROLES,
|
||||
INVITE_TYPES,
|
||||
INVITE_JOIN_TYPES,
|
||||
JOIN_REQUEST_TYPES,
|
||||
JOIN_REQUEST_STATUSES,
|
||||
PERMISSION_KEYS,
|
||||
PLUGIN_API_VERSION,
|
||||
PLUGIN_STATUSES,
|
||||
PLUGIN_CATEGORIES,
|
||||
PLUGIN_CAPABILITIES,
|
||||
PLUGIN_UI_SLOT_TYPES,
|
||||
PLUGIN_UI_SLOT_ENTITY_TYPES,
|
||||
PLUGIN_LAUNCHER_PLACEMENT_ZONES,
|
||||
PLUGIN_LAUNCHER_ACTIONS,
|
||||
PLUGIN_LAUNCHER_BOUNDS,
|
||||
PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS,
|
||||
PLUGIN_STATE_SCOPE_KINDS,
|
||||
PLUGIN_JOB_STATUSES,
|
||||
PLUGIN_JOB_RUN_STATUSES,
|
||||
PLUGIN_JOB_RUN_TRIGGERS,
|
||||
PLUGIN_WEBHOOK_DELIVERY_STATUSES,
|
||||
PLUGIN_EVENT_TYPES,
|
||||
PLUGIN_BRIDGE_ERROR_CODES,
|
||||
type CompanyStatus,
|
||||
type DeploymentMode,
|
||||
type DeploymentExposure,
|
||||
type AuthBaseUrlMode,
|
||||
type AgentStatus,
|
||||
type AgentAdapterType,
|
||||
type AgentRole,
|
||||
type AgentIconName,
|
||||
type IssueStatus,
|
||||
type IssuePriority,
|
||||
type IssueOriginKind,
|
||||
type GoalLevel,
|
||||
type GoalStatus,
|
||||
type ProjectStatus,
|
||||
type RoutineStatus,
|
||||
type RoutineConcurrencyPolicy,
|
||||
type RoutineCatchUpPolicy,
|
||||
type RoutineTriggerKind,
|
||||
type RoutineTriggerSigningMode,
|
||||
type RoutineRunStatus,
|
||||
type RoutineRunSource,
|
||||
type PauseReason,
|
||||
type ApprovalType,
|
||||
type ApprovalStatus,
|
||||
type SecretProvider,
|
||||
type StorageProvider,
|
||||
type BillingType,
|
||||
type FinanceEventKind,
|
||||
type FinanceDirection,
|
||||
type FinanceUnit,
|
||||
type BudgetScopeType,
|
||||
type BudgetMetric,
|
||||
type BudgetWindowKind,
|
||||
type BudgetThresholdType,
|
||||
type BudgetIncidentStatus,
|
||||
type BudgetIncidentResolutionAction,
|
||||
type HeartbeatInvocationSource,
|
||||
type HeartbeatRunStatus,
|
||||
type WakeupTriggerDetail,
|
||||
type WakeupRequestStatus,
|
||||
type LiveEventType,
|
||||
type PrincipalType,
|
||||
type MembershipStatus,
|
||||
type InstanceUserRole,
|
||||
type InviteType,
|
||||
type InviteJoinType,
|
||||
type JoinRequestType,
|
||||
type JoinRequestStatus,
|
||||
type PermissionKey,
|
||||
type PluginStatus,
|
||||
type PluginCategory,
|
||||
type PluginCapability,
|
||||
type PluginUiSlotType,
|
||||
type PluginUiSlotEntityType,
|
||||
type PluginLauncherPlacementZone,
|
||||
type PluginLauncherAction,
|
||||
type PluginLauncherBounds,
|
||||
type PluginLauncherRenderEnvironment,
|
||||
type PluginStateScopeKind,
|
||||
type PluginJobStatus,
|
||||
type PluginJobRunStatus,
|
||||
type PluginJobRunTrigger,
|
||||
type PluginWebhookDeliveryStatus,
|
||||
type PluginEventType,
|
||||
type PluginBridgeErrorCode,
|
||||
} from "./constants.js";
|
||||
|
||||
export type {
|
||||
Company,
|
||||
CompanySkillSourceType,
|
||||
CompanySkillTrustLevel,
|
||||
CompanySkillCompatibility,
|
||||
CompanySkillSourceBadge,
|
||||
CompanySkillFileInventoryEntry,
|
||||
CompanySkill,
|
||||
CompanySkillListItem,
|
||||
CompanySkillUsageAgent,
|
||||
CompanySkillDetail,
|
||||
CompanySkillUpdateStatus,
|
||||
CompanySkillImportRequest,
|
||||
CompanySkillImportResult,
|
||||
CompanySkillProjectScanRequest,
|
||||
CompanySkillProjectScanSkipped,
|
||||
CompanySkillProjectScanConflict,
|
||||
CompanySkillProjectScanResult,
|
||||
CompanySkillCreateRequest,
|
||||
CompanySkillFileDetail,
|
||||
CompanySkillFileUpdateRequest,
|
||||
AgentSkillSyncMode,
|
||||
AgentSkillState,
|
||||
AgentSkillOrigin,
|
||||
AgentSkillEntry,
|
||||
AgentSkillSnapshot,
|
||||
AgentSkillSyncRequest,
|
||||
InstanceExperimentalSettings,
|
||||
InstanceGeneralSettings,
|
||||
InstanceSettings,
|
||||
Agent,
|
||||
AgentAccessState,
|
||||
AgentChainOfCommandEntry,
|
||||
AgentDetail,
|
||||
AgentPermissions,
|
||||
AgentInstructionsBundleMode,
|
||||
AgentInstructionsFileSummary,
|
||||
AgentInstructionsFileDetail,
|
||||
AgentInstructionsBundle,
|
||||
AgentKeyCreated,
|
||||
AgentConfigRevision,
|
||||
AdapterEnvironmentCheckLevel,
|
||||
AdapterEnvironmentTestStatus,
|
||||
AdapterEnvironmentCheck,
|
||||
AdapterEnvironmentTestResult,
|
||||
AssetImage,
|
||||
Project,
|
||||
ProjectCodebase,
|
||||
ProjectCodebaseOrigin,
|
||||
ProjectGoalRef,
|
||||
ProjectWorkspace,
|
||||
ExecutionWorkspace,
|
||||
WorkspaceRuntimeService,
|
||||
WorkspaceOperation,
|
||||
WorkspaceOperationPhase,
|
||||
WorkspaceOperationStatus,
|
||||
ExecutionWorkspaceStrategyType,
|
||||
ExecutionWorkspaceMode,
|
||||
ExecutionWorkspaceProviderType,
|
||||
ExecutionWorkspaceStatus,
|
||||
ExecutionWorkspaceStrategy,
|
||||
ProjectExecutionWorkspacePolicy,
|
||||
ProjectExecutionWorkspaceDefaultMode,
|
||||
IssueExecutionWorkspaceSettings,
|
||||
IssueWorkProduct,
|
||||
IssueWorkProductType,
|
||||
IssueWorkProductProvider,
|
||||
IssueWorkProductStatus,
|
||||
IssueWorkProductReviewState,
|
||||
Issue,
|
||||
IssueAssigneeAdapterOverrides,
|
||||
IssueComment,
|
||||
IssueDocument,
|
||||
IssueDocumentSummary,
|
||||
DocumentRevision,
|
||||
DocumentFormat,
|
||||
LegacyPlanDocument,
|
||||
IssueAttachment,
|
||||
IssueLabel,
|
||||
Goal,
|
||||
Approval,
|
||||
ApprovalComment,
|
||||
BudgetPolicy,
|
||||
BudgetPolicySummary,
|
||||
BudgetIncident,
|
||||
BudgetOverview,
|
||||
BudgetPolicyUpsertInput,
|
||||
BudgetIncidentResolutionInput,
|
||||
CostEvent,
|
||||
CostSummary,
|
||||
CostByAgent,
|
||||
CostByProviderModel,
|
||||
CostByBiller,
|
||||
CostByAgentModel,
|
||||
CostWindowSpendRow,
|
||||
CostByProject,
|
||||
FinanceEvent,
|
||||
FinanceSummary,
|
||||
FinanceByBiller,
|
||||
FinanceByKind,
|
||||
HeartbeatRun,
|
||||
HeartbeatRunEvent,
|
||||
AgentRuntimeState,
|
||||
AgentTaskSession,
|
||||
AgentWakeupRequest,
|
||||
InstanceSchedulerHeartbeatAgent,
|
||||
LiveEvent,
|
||||
DashboardSummary,
|
||||
ActivityEvent,
|
||||
SidebarBadges,
|
||||
CompanyMembership,
|
||||
PrincipalPermissionGrant,
|
||||
Invite,
|
||||
JoinRequest,
|
||||
InstanceUserRoleGrant,
|
||||
CompanyPortabilityInclude,
|
||||
CompanyPortabilityEnvInput,
|
||||
CompanyPortabilityFileEntry,
|
||||
CompanyPortabilityCompanyManifestEntry,
|
||||
CompanyPortabilitySidebarOrder,
|
||||
CompanyPortabilityAgentManifestEntry,
|
||||
CompanyPortabilitySkillManifestEntry,
|
||||
CompanyPortabilityProjectManifestEntry,
|
||||
CompanyPortabilityProjectWorkspaceManifestEntry,
|
||||
CompanyPortabilityIssueRoutineTriggerManifestEntry,
|
||||
CompanyPortabilityIssueRoutineManifestEntry,
|
||||
CompanyPortabilityIssueManifestEntry,
|
||||
CompanyPortabilityManifest,
|
||||
CompanyPortabilityExportResult,
|
||||
CompanyPortabilityExportPreviewFile,
|
||||
CompanyPortabilityExportPreviewResult,
|
||||
CompanyPortabilitySource,
|
||||
CompanyPortabilityImportTarget,
|
||||
CompanyPortabilityAgentSelection,
|
||||
CompanyPortabilityCollisionStrategy,
|
||||
CompanyPortabilityPreviewRequest,
|
||||
CompanyPortabilityPreviewAgentPlan,
|
||||
CompanyPortabilityPreviewProjectPlan,
|
||||
CompanyPortabilityPreviewIssuePlan,
|
||||
CompanyPortabilityPreviewResult,
|
||||
CompanyPortabilityAdapterOverride,
|
||||
CompanyPortabilityImportRequest,
|
||||
CompanyPortabilityImportResult,
|
||||
CompanyPortabilityExportRequest,
|
||||
EnvBinding,
|
||||
AgentEnvConfig,
|
||||
CompanySecret,
|
||||
SecretProviderDescriptor,
|
||||
Routine,
|
||||
RoutineTrigger,
|
||||
RoutineRun,
|
||||
RoutineTriggerSecretMaterial,
|
||||
RoutineDetail,
|
||||
RoutineRunSummary,
|
||||
RoutineExecutionIssueOrigin,
|
||||
RoutineListItem,
|
||||
JsonSchema,
|
||||
PluginJobDeclaration,
|
||||
PluginWebhookDeclaration,
|
||||
PluginToolDeclaration,
|
||||
PluginUiSlotDeclaration,
|
||||
PluginLauncherActionDeclaration,
|
||||
PluginLauncherRenderDeclaration,
|
||||
PluginLauncherRenderContextSnapshot,
|
||||
PluginLauncherDeclaration,
|
||||
PluginMinimumHostVersion,
|
||||
PluginUiDeclaration,
|
||||
PaperclipPluginManifestV1,
|
||||
PluginRecord,
|
||||
PluginStateRecord,
|
||||
PluginConfig,
|
||||
PluginEntityRecord,
|
||||
PluginEntityQuery,
|
||||
PluginJobRecord,
|
||||
PluginJobRunRecord,
|
||||
PluginWebhookDeliveryRecord,
|
||||
QuotaWindow,
|
||||
ProviderQuotaResult,
|
||||
} from "./types/index.js";
|
||||
|
||||
export {
|
||||
instanceGeneralSettingsSchema,
|
||||
patchInstanceGeneralSettingsSchema,
|
||||
type PatchInstanceGeneralSettings,
|
||||
instanceExperimentalSettingsSchema,
|
||||
patchInstanceExperimentalSettingsSchema,
|
||||
type PatchInstanceExperimentalSettings,
|
||||
} from "./validators/index.js";
|
||||
|
||||
export {
|
||||
createCompanySchema,
|
||||
updateCompanySchema,
|
||||
updateCompanyBrandingSchema,
|
||||
type CreateCompany,
|
||||
type UpdateCompany,
|
||||
type UpdateCompanyBranding,
|
||||
agentSkillStateSchema,
|
||||
agentSkillSyncModeSchema,
|
||||
agentSkillEntrySchema,
|
||||
agentSkillSnapshotSchema,
|
||||
agentSkillSyncSchema,
|
||||
type AgentSkillSync,
|
||||
createAgentSchema,
|
||||
createAgentHireSchema,
|
||||
updateAgentSchema,
|
||||
agentInstructionsBundleModeSchema,
|
||||
updateAgentInstructionsBundleSchema,
|
||||
upsertAgentInstructionsFileSchema,
|
||||
updateAgentInstructionsPathSchema,
|
||||
createAgentKeySchema,
|
||||
wakeAgentSchema,
|
||||
resetAgentSessionSchema,
|
||||
testAdapterEnvironmentSchema,
|
||||
agentPermissionsSchema,
|
||||
updateAgentPermissionsSchema,
|
||||
type CreateAgent,
|
||||
type CreateAgentHire,
|
||||
type UpdateAgent,
|
||||
type UpdateAgentInstructionsBundle,
|
||||
type UpsertAgentInstructionsFile,
|
||||
type UpdateAgentInstructionsPath,
|
||||
type CreateAgentKey,
|
||||
type WakeAgent,
|
||||
type ResetAgentSession,
|
||||
type TestAdapterEnvironment,
|
||||
type UpdateAgentPermissions,
|
||||
createProjectSchema,
|
||||
updateProjectSchema,
|
||||
createProjectWorkspaceSchema,
|
||||
updateProjectWorkspaceSchema,
|
||||
type CreateProject,
|
||||
type UpdateProject,
|
||||
type CreateProjectWorkspace,
|
||||
type UpdateProjectWorkspace,
|
||||
projectExecutionWorkspacePolicySchema,
|
||||
createIssueSchema,
|
||||
createIssueLabelSchema,
|
||||
updateIssueSchema,
|
||||
issueExecutionWorkspaceSettingsSchema,
|
||||
checkoutIssueSchema,
|
||||
addIssueCommentSchema,
|
||||
linkIssueApprovalSchema,
|
||||
createIssueAttachmentMetadataSchema,
|
||||
createIssueWorkProductSchema,
|
||||
updateIssueWorkProductSchema,
|
||||
issueWorkProductTypeSchema,
|
||||
issueWorkProductStatusSchema,
|
||||
issueWorkProductReviewStateSchema,
|
||||
updateExecutionWorkspaceSchema,
|
||||
executionWorkspaceStatusSchema,
|
||||
issueDocumentFormatSchema,
|
||||
issueDocumentKeySchema,
|
||||
upsertIssueDocumentSchema,
|
||||
type CreateIssue,
|
||||
type CreateIssueLabel,
|
||||
type UpdateIssue,
|
||||
type CheckoutIssue,
|
||||
type AddIssueComment,
|
||||
type LinkIssueApproval,
|
||||
type CreateIssueAttachmentMetadata,
|
||||
type CreateIssueWorkProduct,
|
||||
type UpdateIssueWorkProduct,
|
||||
type UpdateExecutionWorkspace,
|
||||
type IssueDocumentFormat,
|
||||
type UpsertIssueDocument,
|
||||
createGoalSchema,
|
||||
updateGoalSchema,
|
||||
type CreateGoal,
|
||||
type UpdateGoal,
|
||||
createApprovalSchema,
|
||||
upsertBudgetPolicySchema,
|
||||
resolveBudgetIncidentSchema,
|
||||
resolveApprovalSchema,
|
||||
requestApprovalRevisionSchema,
|
||||
resubmitApprovalSchema,
|
||||
addApprovalCommentSchema,
|
||||
type CreateApproval,
|
||||
type UpsertBudgetPolicy,
|
||||
type ResolveBudgetIncident,
|
||||
type ResolveApproval,
|
||||
type RequestApprovalRevision,
|
||||
type ResubmitApproval,
|
||||
type AddApprovalComment,
|
||||
envBindingPlainSchema,
|
||||
envBindingSecretRefSchema,
|
||||
envBindingSchema,
|
||||
envConfigSchema,
|
||||
createSecretSchema,
|
||||
rotateSecretSchema,
|
||||
updateSecretSchema,
|
||||
createRoutineSchema,
|
||||
updateRoutineSchema,
|
||||
createRoutineTriggerSchema,
|
||||
updateRoutineTriggerSchema,
|
||||
runRoutineSchema,
|
||||
rotateRoutineTriggerSecretSchema,
|
||||
type CreateSecret,
|
||||
type RotateSecret,
|
||||
type UpdateSecret,
|
||||
type CreateRoutine,
|
||||
type UpdateRoutine,
|
||||
type CreateRoutineTrigger,
|
||||
type UpdateRoutineTrigger,
|
||||
type RunRoutine,
|
||||
type RotateRoutineTriggerSecret,
|
||||
createCostEventSchema,
|
||||
createFinanceEventSchema,
|
||||
updateBudgetSchema,
|
||||
createAssetImageMetadataSchema,
|
||||
createCompanyInviteSchema,
|
||||
createOpenClawInvitePromptSchema,
|
||||
acceptInviteSchema,
|
||||
listJoinRequestsQuerySchema,
|
||||
claimJoinRequestApiKeySchema,
|
||||
boardCliAuthAccessLevelSchema,
|
||||
createCliAuthChallengeSchema,
|
||||
resolveCliAuthChallengeSchema,
|
||||
updateMemberPermissionsSchema,
|
||||
updateUserCompanyAccessSchema,
|
||||
type CreateCostEvent,
|
||||
type CreateFinanceEvent,
|
||||
type UpdateBudget,
|
||||
type CreateAssetImageMetadata,
|
||||
type CreateCompanyInvite,
|
||||
type CreateOpenClawInvitePrompt,
|
||||
type AcceptInvite,
|
||||
type ListJoinRequestsQuery,
|
||||
type ClaimJoinRequestApiKey,
|
||||
type BoardCliAuthAccessLevel,
|
||||
type CreateCliAuthChallenge,
|
||||
type ResolveCliAuthChallenge,
|
||||
type UpdateMemberPermissions,
|
||||
type UpdateUserCompanyAccess,
|
||||
companySkillSourceTypeSchema,
|
||||
companySkillTrustLevelSchema,
|
||||
companySkillCompatibilitySchema,
|
||||
companySkillSourceBadgeSchema,
|
||||
companySkillFileInventoryEntrySchema,
|
||||
companySkillSchema,
|
||||
companySkillListItemSchema,
|
||||
companySkillUsageAgentSchema,
|
||||
companySkillDetailSchema,
|
||||
companySkillUpdateStatusSchema,
|
||||
companySkillImportSchema,
|
||||
companySkillProjectScanRequestSchema,
|
||||
companySkillProjectScanSkippedSchema,
|
||||
companySkillProjectScanConflictSchema,
|
||||
companySkillProjectScanResultSchema,
|
||||
companySkillCreateSchema,
|
||||
companySkillFileDetailSchema,
|
||||
companySkillFileUpdateSchema,
|
||||
portabilityIncludeSchema,
|
||||
portabilityEnvInputSchema,
|
||||
portabilityCompanyManifestEntrySchema,
|
||||
portabilitySidebarOrderSchema,
|
||||
portabilityAgentManifestEntrySchema,
|
||||
portabilityManifestSchema,
|
||||
portabilitySourceSchema,
|
||||
portabilityTargetSchema,
|
||||
portabilityAgentSelectionSchema,
|
||||
portabilityCollisionStrategySchema,
|
||||
companyPortabilityExportSchema,
|
||||
companyPortabilityPreviewSchema,
|
||||
companyPortabilityImportSchema,
|
||||
type CompanyPortabilityExport,
|
||||
type CompanyPortabilityPreview,
|
||||
type CompanyPortabilityImport,
|
||||
jsonSchemaSchema,
|
||||
pluginJobDeclarationSchema,
|
||||
pluginWebhookDeclarationSchema,
|
||||
pluginToolDeclarationSchema,
|
||||
pluginUiSlotDeclarationSchema,
|
||||
pluginLauncherActionDeclarationSchema,
|
||||
pluginLauncherRenderDeclarationSchema,
|
||||
pluginLauncherDeclarationSchema,
|
||||
pluginManifestV1Schema,
|
||||
installPluginSchema,
|
||||
upsertPluginConfigSchema,
|
||||
patchPluginConfigSchema,
|
||||
updatePluginStatusSchema,
|
||||
uninstallPluginSchema,
|
||||
pluginStateScopeKeySchema,
|
||||
setPluginStateSchema,
|
||||
listPluginStateSchema,
|
||||
type PluginJobDeclarationInput,
|
||||
type PluginWebhookDeclarationInput,
|
||||
type PluginToolDeclarationInput,
|
||||
type PluginUiSlotDeclarationInput,
|
||||
type PluginLauncherActionDeclarationInput,
|
||||
type PluginLauncherRenderDeclarationInput,
|
||||
type PluginLauncherDeclarationInput,
|
||||
type PluginManifestV1Input,
|
||||
type InstallPlugin,
|
||||
type UpsertPluginConfig,
|
||||
type PatchPluginConfig,
|
||||
type UpdatePluginStatus,
|
||||
type UninstallPlugin,
|
||||
type PluginStateScopeKey,
|
||||
type SetPluginState,
|
||||
type ListPluginState,
|
||||
} from "./validators/index.js";
|
||||
|
||||
export { API_PREFIX, API } from "./api.js";
|
||||
export { normalizeAgentUrlKey, deriveAgentUrlKey, isUuidLike } from "./agent-url-key.js";
|
||||
export { deriveProjectUrlKey, normalizeProjectUrlKey } from "./project-url-key.js";
|
||||
export {
|
||||
AGENT_MENTION_SCHEME,
|
||||
PROJECT_MENTION_SCHEME,
|
||||
buildAgentMentionHref,
|
||||
buildProjectMentionHref,
|
||||
extractAgentMentionIds,
|
||||
parseAgentMentionHref,
|
||||
parseProjectMentionHref,
|
||||
extractProjectMentionIds,
|
||||
type ParsedAgentMention,
|
||||
type ParsedProjectMention,
|
||||
} from "./project-mentions.js";
|
||||
|
||||
export {
|
||||
paperclipConfigSchema,
|
||||
configMetaSchema,
|
||||
llmConfigSchema,
|
||||
databaseBackupConfigSchema,
|
||||
databaseConfigSchema,
|
||||
loggingConfigSchema,
|
||||
serverConfigSchema,
|
||||
authConfigSchema,
|
||||
secretsConfigSchema,
|
||||
storageConfigSchema,
|
||||
storageLocalDiskConfigSchema,
|
||||
storageS3ConfigSchema,
|
||||
secretsLocalEncryptedConfigSchema,
|
||||
type PaperclipConfig,
|
||||
type LlmConfig,
|
||||
type DatabaseBackupConfig,
|
||||
type DatabaseConfig,
|
||||
type LoggingConfig,
|
||||
type ServerConfig,
|
||||
type AuthConfig,
|
||||
type StorageConfig,
|
||||
type StorageLocalDiskConfig,
|
||||
type StorageS3Config,
|
||||
type SecretsConfig,
|
||||
type SecretsLocalEncryptedConfig,
|
||||
type ConfigMeta,
|
||||
} from "./config-schema.js";
|
||||
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildAgentMentionHref,
|
||||
buildProjectMentionHref,
|
||||
extractAgentMentionIds,
|
||||
extractProjectMentionIds,
|
||||
parseAgentMentionHref,
|
||||
parseProjectMentionHref,
|
||||
} from "./project-mentions.js";
|
||||
|
||||
describe("project-mentions", () => {
|
||||
it("round-trips project mentions with color metadata", () => {
|
||||
const href = buildProjectMentionHref("project-123", "#336699");
|
||||
expect(parseProjectMentionHref(href)).toEqual({
|
||||
projectId: "project-123",
|
||||
color: "#336699",
|
||||
});
|
||||
expect(extractProjectMentionIds(`[@Paperclip App](${href})`)).toEqual(["project-123"]);
|
||||
});
|
||||
|
||||
it("round-trips agent mentions with icon metadata", () => {
|
||||
const href = buildAgentMentionHref("agent-123", "code");
|
||||
expect(parseAgentMentionHref(href)).toEqual({
|
||||
agentId: "agent-123",
|
||||
icon: "code",
|
||||
});
|
||||
expect(extractAgentMentionIds(`[@CodexCoder](${href})`)).toEqual(["agent-123"]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,135 @@
|
||||
export const PROJECT_MENTION_SCHEME = "project://";
|
||||
export const AGENT_MENTION_SCHEME = "agent://";
|
||||
|
||||
const HEX_COLOR_RE = /^[0-9a-f]{6}$/i;
|
||||
const HEX_COLOR_SHORT_RE = /^[0-9a-f]{3}$/i;
|
||||
const HEX_COLOR_WITH_HASH_RE = /^#[0-9a-f]{6}$/i;
|
||||
const HEX_COLOR_SHORT_WITH_HASH_RE = /^#[0-9a-f]{3}$/i;
|
||||
const PROJECT_MENTION_LINK_RE = /\[[^\]]*]\((project:\/\/[^)\s]+)\)/gi;
|
||||
const AGENT_MENTION_LINK_RE = /\[[^\]]*]\((agent:\/\/[^)\s]+)\)/gi;
|
||||
const AGENT_ICON_NAME_RE = /^[a-z0-9-]+$/i;
|
||||
|
||||
export interface ParsedProjectMention {
|
||||
projectId: string;
|
||||
color: string | null;
|
||||
}
|
||||
|
||||
export interface ParsedAgentMention {
|
||||
agentId: string;
|
||||
icon: string | null;
|
||||
}
|
||||
|
||||
function normalizeHexColor(input: string | null | undefined): string | null {
|
||||
if (!input) return null;
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
if (HEX_COLOR_WITH_HASH_RE.test(trimmed)) {
|
||||
return trimmed.toLowerCase();
|
||||
}
|
||||
if (HEX_COLOR_RE.test(trimmed)) {
|
||||
return `#${trimmed.toLowerCase()}`;
|
||||
}
|
||||
if (HEX_COLOR_SHORT_WITH_HASH_RE.test(trimmed)) {
|
||||
const raw = trimmed.slice(1).toLowerCase();
|
||||
return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;
|
||||
}
|
||||
if (HEX_COLOR_SHORT_RE.test(trimmed)) {
|
||||
const raw = trimmed.toLowerCase();
|
||||
return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function buildProjectMentionHref(projectId: string, color?: string | null): string {
|
||||
const trimmedProjectId = projectId.trim();
|
||||
const normalizedColor = normalizeHexColor(color ?? null);
|
||||
if (!normalizedColor) {
|
||||
return `${PROJECT_MENTION_SCHEME}${trimmedProjectId}`;
|
||||
}
|
||||
return `${PROJECT_MENTION_SCHEME}${trimmedProjectId}?c=${encodeURIComponent(normalizedColor.slice(1))}`;
|
||||
}
|
||||
|
||||
export function parseProjectMentionHref(href: string): ParsedProjectMention | null {
|
||||
if (!href.startsWith(PROJECT_MENTION_SCHEME)) return null;
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(href);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (url.protocol !== "project:") return null;
|
||||
|
||||
const projectId = `${url.hostname}${url.pathname}`.replace(/^\/+/, "").trim();
|
||||
if (!projectId) return null;
|
||||
|
||||
const color = normalizeHexColor(url.searchParams.get("c") ?? url.searchParams.get("color"));
|
||||
|
||||
return {
|
||||
projectId,
|
||||
color,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAgentMentionHref(agentId: string, icon?: string | null): string {
|
||||
const trimmedAgentId = agentId.trim();
|
||||
const normalizedIcon = normalizeAgentIcon(icon ?? null);
|
||||
if (!normalizedIcon) {
|
||||
return `${AGENT_MENTION_SCHEME}${trimmedAgentId}`;
|
||||
}
|
||||
return `${AGENT_MENTION_SCHEME}${trimmedAgentId}?i=${encodeURIComponent(normalizedIcon)}`;
|
||||
}
|
||||
|
||||
export function parseAgentMentionHref(href: string): ParsedAgentMention | null {
|
||||
if (!href.startsWith(AGENT_MENTION_SCHEME)) return null;
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(href);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (url.protocol !== "agent:") return null;
|
||||
|
||||
const agentId = `${url.hostname}${url.pathname}`.replace(/^\/+/, "").trim();
|
||||
if (!agentId) return null;
|
||||
|
||||
return {
|
||||
agentId,
|
||||
icon: normalizeAgentIcon(url.searchParams.get("i") ?? url.searchParams.get("icon")),
|
||||
};
|
||||
}
|
||||
|
||||
export function extractProjectMentionIds(markdown: string): string[] {
|
||||
if (!markdown) return [];
|
||||
const ids = new Set<string>();
|
||||
const re = new RegExp(PROJECT_MENTION_LINK_RE);
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = re.exec(markdown)) !== null) {
|
||||
const parsed = parseProjectMentionHref(match[1]);
|
||||
if (parsed) ids.add(parsed.projectId);
|
||||
}
|
||||
return [...ids];
|
||||
}
|
||||
|
||||
export function extractAgentMentionIds(markdown: string): string[] {
|
||||
if (!markdown) return [];
|
||||
const ids = new Set<string>();
|
||||
const re = new RegExp(AGENT_MENTION_LINK_RE);
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = re.exec(markdown)) !== null) {
|
||||
const parsed = parseAgentMentionHref(match[1]);
|
||||
if (parsed) ids.add(parsed.agentId);
|
||||
}
|
||||
return [...ids];
|
||||
}
|
||||
|
||||
function normalizeAgentIcon(input: string | null | undefined): string | null {
|
||||
if (!input) return null;
|
||||
const trimmed = input.trim().toLowerCase();
|
||||
if (!trimmed || !AGENT_ICON_NAME_RE.test(trimmed)) return null;
|
||||
return trimmed;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
const PROJECT_URL_KEY_DELIM_RE = /[^a-z0-9]+/g;
|
||||
const PROJECT_URL_KEY_TRIM_RE = /^-+|-+$/g;
|
||||
|
||||
export function normalizeProjectUrlKey(value: string | null | undefined): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
const normalized = value
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(PROJECT_URL_KEY_DELIM_RE, "-")
|
||||
.replace(PROJECT_URL_KEY_TRIM_RE, "");
|
||||
return normalized.length > 0 ? normalized : null;
|
||||
}
|
||||
|
||||
export function deriveProjectUrlKey(name: string | null | undefined, fallback?: string | null): string {
|
||||
return normalizeProjectUrlKey(name) ?? normalizeProjectUrlKey(fallback) ?? "project";
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import type {
|
||||
AgentAdapterType,
|
||||
InstanceUserRole,
|
||||
InviteJoinType,
|
||||
InviteType,
|
||||
JoinRequestStatus,
|
||||
JoinRequestType,
|
||||
MembershipStatus,
|
||||
PermissionKey,
|
||||
PrincipalType,
|
||||
} from "../constants.js";
|
||||
|
||||
export interface CompanyMembership {
|
||||
id: string;
|
||||
companyId: string;
|
||||
principalType: PrincipalType;
|
||||
principalId: string;
|
||||
status: MembershipStatus;
|
||||
membershipRole: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface PrincipalPermissionGrant {
|
||||
id: string;
|
||||
companyId: string;
|
||||
principalType: PrincipalType;
|
||||
principalId: string;
|
||||
permissionKey: PermissionKey;
|
||||
scope: Record<string, unknown> | null;
|
||||
grantedByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface Invite {
|
||||
id: string;
|
||||
companyId: string | null;
|
||||
inviteType: InviteType;
|
||||
tokenHash: string;
|
||||
allowedJoinTypes: InviteJoinType;
|
||||
defaultsPayload: Record<string, unknown> | null;
|
||||
expiresAt: Date;
|
||||
invitedByUserId: string | null;
|
||||
revokedAt: Date | null;
|
||||
acceptedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface JoinRequest {
|
||||
id: string;
|
||||
inviteId: string;
|
||||
companyId: string;
|
||||
requestType: JoinRequestType;
|
||||
status: JoinRequestStatus;
|
||||
requestIp: string;
|
||||
requestingUserId: string | null;
|
||||
requestEmailSnapshot: string | null;
|
||||
agentName: string | null;
|
||||
adapterType: AgentAdapterType | null;
|
||||
capabilities: string | null;
|
||||
agentDefaultsPayload: Record<string, unknown> | null;
|
||||
claimSecretExpiresAt: Date | null;
|
||||
claimSecretConsumedAt: Date | null;
|
||||
createdAgentId: string | null;
|
||||
approvedByUserId: string | null;
|
||||
approvedAt: Date | null;
|
||||
rejectedByUserId: string | null;
|
||||
rejectedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface InstanceUserRoleGrant {
|
||||
id: string;
|
||||
userId: string;
|
||||
role: InstanceUserRole;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface ActivityEvent {
|
||||
id: string;
|
||||
companyId: string;
|
||||
actorType: "agent" | "user" | "system";
|
||||
actorId: string;
|
||||
action: string;
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
agentId: string | null;
|
||||
runId: string | null;
|
||||
details: Record<string, unknown> | null;
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
export type AgentSkillSyncMode = "unsupported" | "persistent" | "ephemeral";
|
||||
|
||||
export type AgentSkillState =
|
||||
| "available"
|
||||
| "configured"
|
||||
| "installed"
|
||||
| "missing"
|
||||
| "stale"
|
||||
| "external";
|
||||
|
||||
export type AgentSkillOrigin =
|
||||
| "company_managed"
|
||||
| "paperclip_required"
|
||||
| "user_installed"
|
||||
| "external_unknown";
|
||||
|
||||
export interface AgentSkillEntry {
|
||||
key: string;
|
||||
runtimeName: string | null;
|
||||
desired: boolean;
|
||||
managed: boolean;
|
||||
required?: boolean;
|
||||
requiredReason?: string | null;
|
||||
state: AgentSkillState;
|
||||
origin?: AgentSkillOrigin;
|
||||
originLabel?: string | null;
|
||||
locationLabel?: string | null;
|
||||
readOnly?: boolean;
|
||||
sourcePath?: string | null;
|
||||
targetPath?: string | null;
|
||||
detail?: string | null;
|
||||
}
|
||||
|
||||
export interface AgentSkillSnapshot {
|
||||
adapterType: string;
|
||||
supported: boolean;
|
||||
mode: AgentSkillSyncMode;
|
||||
desiredSkills: string[];
|
||||
entries: AgentSkillEntry[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface AgentSkillSyncRequest {
|
||||
desiredSkills: string[];
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import type {
|
||||
AgentAdapterType,
|
||||
PauseReason,
|
||||
AgentRole,
|
||||
AgentStatus,
|
||||
} from "../constants.js";
|
||||
import type {
|
||||
CompanyMembership,
|
||||
PrincipalPermissionGrant,
|
||||
} from "./access.js";
|
||||
|
||||
export interface AgentPermissions {
|
||||
canCreateAgents: boolean;
|
||||
}
|
||||
|
||||
export type AgentInstructionsBundleMode = "managed" | "external";
|
||||
|
||||
export interface AgentInstructionsFileSummary {
|
||||
path: string;
|
||||
size: number;
|
||||
language: string;
|
||||
markdown: boolean;
|
||||
isEntryFile: boolean;
|
||||
editable: boolean;
|
||||
deprecated: boolean;
|
||||
virtual: boolean;
|
||||
}
|
||||
|
||||
export interface AgentInstructionsFileDetail extends AgentInstructionsFileSummary {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface AgentInstructionsBundle {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
mode: AgentInstructionsBundleMode | null;
|
||||
rootPath: string | null;
|
||||
managedRootPath: string;
|
||||
entryFile: string;
|
||||
resolvedEntryPath: string | null;
|
||||
editable: boolean;
|
||||
warnings: string[];
|
||||
legacyPromptTemplateActive: boolean;
|
||||
legacyBootstrapPromptTemplateActive: boolean;
|
||||
files: AgentInstructionsFileSummary[];
|
||||
}
|
||||
|
||||
export interface AgentAccessState {
|
||||
canAssignTasks: boolean;
|
||||
taskAssignSource: "explicit_grant" | "agent_creator" | "ceo_role" | "none";
|
||||
membership: CompanyMembership | null;
|
||||
grants: PrincipalPermissionGrant[];
|
||||
}
|
||||
|
||||
export interface AgentChainOfCommandEntry {
|
||||
id: string;
|
||||
name: string;
|
||||
role: AgentRole;
|
||||
title: string | null;
|
||||
}
|
||||
|
||||
export interface Agent {
|
||||
id: string;
|
||||
companyId: string;
|
||||
name: string;
|
||||
urlKey: string;
|
||||
role: AgentRole;
|
||||
title: string | null;
|
||||
icon: string | null;
|
||||
status: AgentStatus;
|
||||
reportsTo: string | null;
|
||||
capabilities: string | null;
|
||||
adapterType: AgentAdapterType;
|
||||
adapterConfig: Record<string, unknown>;
|
||||
runtimeConfig: Record<string, unknown>;
|
||||
budgetMonthlyCents: number;
|
||||
spentMonthlyCents: number;
|
||||
pauseReason: PauseReason | null;
|
||||
pausedAt: Date | null;
|
||||
permissions: AgentPermissions;
|
||||
lastHeartbeatAt: Date | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface AgentDetail extends Agent {
|
||||
chainOfCommand: AgentChainOfCommandEntry[];
|
||||
access: AgentAccessState;
|
||||
}
|
||||
|
||||
export interface AgentKeyCreated {
|
||||
id: string;
|
||||
name: string;
|
||||
token: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface AgentConfigRevision {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
source: string;
|
||||
rolledBackFromRevisionId: string | null;
|
||||
changedKeys: string[];
|
||||
beforeConfig: Record<string, unknown>;
|
||||
afterConfig: Record<string, unknown>;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export type AdapterEnvironmentCheckLevel = "info" | "warn" | "error";
|
||||
export type AdapterEnvironmentTestStatus = "pass" | "warn" | "fail";
|
||||
|
||||
export interface AdapterEnvironmentCheck {
|
||||
code: string;
|
||||
level: AdapterEnvironmentCheckLevel;
|
||||
message: string;
|
||||
detail?: string | null;
|
||||
hint?: string | null;
|
||||
}
|
||||
|
||||
export interface AdapterEnvironmentTestResult {
|
||||
adapterType: string;
|
||||
status: AdapterEnvironmentTestStatus;
|
||||
checks: AdapterEnvironmentCheck[];
|
||||
testedAt: string;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { ApprovalStatus, ApprovalType } from "../constants.js";
|
||||
|
||||
export interface Approval {
|
||||
id: string;
|
||||
companyId: string;
|
||||
type: ApprovalType;
|
||||
requestedByAgentId: string | null;
|
||||
requestedByUserId: string | null;
|
||||
status: ApprovalStatus;
|
||||
payload: Record<string, unknown>;
|
||||
decisionNote: string | null;
|
||||
decidedByUserId: string | null;
|
||||
decidedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface ApprovalComment {
|
||||
id: string;
|
||||
companyId: string;
|
||||
approvalId: string;
|
||||
authorAgentId: string | null;
|
||||
authorUserId: string | null;
|
||||
body: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface AssetImage {
|
||||
assetId: string;
|
||||
companyId: string;
|
||||
provider: string;
|
||||
objectKey: string;
|
||||
contentType: string;
|
||||
byteSize: number;
|
||||
sha256: string;
|
||||
originalFilename: string | null;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
contentPath: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import type {
|
||||
BudgetIncidentResolutionAction,
|
||||
BudgetIncidentStatus,
|
||||
BudgetMetric,
|
||||
BudgetScopeType,
|
||||
BudgetThresholdType,
|
||||
BudgetWindowKind,
|
||||
PauseReason,
|
||||
} from "../constants.js";
|
||||
|
||||
export interface BudgetPolicy {
|
||||
id: string;
|
||||
companyId: string;
|
||||
scopeType: BudgetScopeType;
|
||||
scopeId: string;
|
||||
metric: BudgetMetric;
|
||||
windowKind: BudgetWindowKind;
|
||||
amount: number;
|
||||
warnPercent: number;
|
||||
hardStopEnabled: boolean;
|
||||
notifyEnabled: boolean;
|
||||
isActive: boolean;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface BudgetPolicySummary {
|
||||
policyId: string;
|
||||
companyId: string;
|
||||
scopeType: BudgetScopeType;
|
||||
scopeId: string;
|
||||
scopeName: string;
|
||||
metric: BudgetMetric;
|
||||
windowKind: BudgetWindowKind;
|
||||
amount: number;
|
||||
observedAmount: number;
|
||||
remainingAmount: number;
|
||||
utilizationPercent: number;
|
||||
warnPercent: number;
|
||||
hardStopEnabled: boolean;
|
||||
notifyEnabled: boolean;
|
||||
isActive: boolean;
|
||||
status: "ok" | "warning" | "hard_stop";
|
||||
paused: boolean;
|
||||
pauseReason: PauseReason | null;
|
||||
windowStart: Date;
|
||||
windowEnd: Date;
|
||||
}
|
||||
|
||||
export interface BudgetIncident {
|
||||
id: string;
|
||||
companyId: string;
|
||||
policyId: string;
|
||||
scopeType: BudgetScopeType;
|
||||
scopeId: string;
|
||||
scopeName: string;
|
||||
metric: BudgetMetric;
|
||||
windowKind: BudgetWindowKind;
|
||||
windowStart: Date;
|
||||
windowEnd: Date;
|
||||
thresholdType: BudgetThresholdType;
|
||||
amountLimit: number;
|
||||
amountObserved: number;
|
||||
status: BudgetIncidentStatus;
|
||||
approvalId: string | null;
|
||||
approvalStatus: string | null;
|
||||
resolvedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface BudgetOverview {
|
||||
companyId: string;
|
||||
policies: BudgetPolicySummary[];
|
||||
activeIncidents: BudgetIncident[];
|
||||
pausedAgentCount: number;
|
||||
pausedProjectCount: number;
|
||||
pendingApprovalCount: number;
|
||||
}
|
||||
|
||||
export interface BudgetPolicyUpsertInput {
|
||||
scopeType: BudgetScopeType;
|
||||
scopeId: string;
|
||||
metric?: BudgetMetric;
|
||||
windowKind?: BudgetWindowKind;
|
||||
amount: number;
|
||||
warnPercent?: number;
|
||||
hardStopEnabled?: boolean;
|
||||
notifyEnabled?: boolean;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface BudgetIncidentResolutionInput {
|
||||
action: BudgetIncidentResolutionAction;
|
||||
amount?: number;
|
||||
decisionNote?: string | null;
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
export interface CompanyPortabilityInclude {
|
||||
company: boolean;
|
||||
agents: boolean;
|
||||
projects: boolean;
|
||||
issues: boolean;
|
||||
skills: boolean;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityEnvInput {
|
||||
key: string;
|
||||
description: string | null;
|
||||
agentSlug: string | null;
|
||||
kind: "secret" | "plain";
|
||||
requirement: "required" | "optional";
|
||||
defaultValue: string | null;
|
||||
portability: "portable" | "system_dependent";
|
||||
}
|
||||
|
||||
export type CompanyPortabilityFileEntry =
|
||||
| string
|
||||
| {
|
||||
encoding: "base64";
|
||||
data: string;
|
||||
contentType?: string | null;
|
||||
};
|
||||
|
||||
export interface CompanyPortabilityCompanyManifestEntry {
|
||||
path: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
brandColor: string | null;
|
||||
logoPath: string | null;
|
||||
requireBoardApprovalForNewAgents: boolean;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilitySidebarOrder {
|
||||
agents: string[];
|
||||
projects: string[];
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityProjectManifestEntry {
|
||||
slug: string;
|
||||
name: string;
|
||||
path: string;
|
||||
description: string | null;
|
||||
ownerAgentSlug: string | null;
|
||||
leadAgentSlug: string | null;
|
||||
targetDate: string | null;
|
||||
color: string | null;
|
||||
status: string | null;
|
||||
executionWorkspacePolicy: Record<string, unknown> | null;
|
||||
workspaces: CompanyPortabilityProjectWorkspaceManifestEntry[];
|
||||
metadata: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityProjectWorkspaceManifestEntry {
|
||||
key: string;
|
||||
name: string;
|
||||
sourceType: string | null;
|
||||
repoUrl: string | null;
|
||||
repoRef: string | null;
|
||||
defaultRef: string | null;
|
||||
visibility: string | null;
|
||||
setupCommand: string | null;
|
||||
cleanupCommand: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
isPrimary: boolean;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityIssueRoutineTriggerManifestEntry {
|
||||
kind: string;
|
||||
label: string | null;
|
||||
enabled: boolean;
|
||||
cronExpression: string | null;
|
||||
timezone: string | null;
|
||||
signingMode: string | null;
|
||||
replayWindowSec: number | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityIssueRoutineManifestEntry {
|
||||
concurrencyPolicy: string | null;
|
||||
catchUpPolicy: string | null;
|
||||
triggers: CompanyPortabilityIssueRoutineTriggerManifestEntry[];
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityIssueManifestEntry {
|
||||
slug: string;
|
||||
identifier: string | null;
|
||||
title: string;
|
||||
path: string;
|
||||
projectSlug: string | null;
|
||||
projectWorkspaceKey: string | null;
|
||||
assigneeAgentSlug: string | null;
|
||||
description: string | null;
|
||||
recurring: boolean;
|
||||
routine: CompanyPortabilityIssueRoutineManifestEntry | null;
|
||||
legacyRecurrence: Record<string, unknown> | null;
|
||||
status: string | null;
|
||||
priority: string | null;
|
||||
labelIds: string[];
|
||||
billingCode: string | null;
|
||||
executionWorkspaceSettings: Record<string, unknown> | null;
|
||||
assigneeAdapterOverrides: Record<string, unknown> | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityAgentManifestEntry {
|
||||
slug: string;
|
||||
name: string;
|
||||
path: string;
|
||||
skills: string[];
|
||||
role: string;
|
||||
title: string | null;
|
||||
icon: string | null;
|
||||
capabilities: string | null;
|
||||
reportsToSlug: string | null;
|
||||
adapterType: string;
|
||||
adapterConfig: Record<string, unknown>;
|
||||
runtimeConfig: Record<string, unknown>;
|
||||
permissions: Record<string, unknown>;
|
||||
budgetMonthlyCents: number;
|
||||
metadata: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilitySkillManifestEntry {
|
||||
key: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
path: string;
|
||||
description: string | null;
|
||||
sourceType: string;
|
||||
sourceLocator: string | null;
|
||||
sourceRef: string | null;
|
||||
trustLevel: string | null;
|
||||
compatibility: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
fileInventory: Array<{
|
||||
path: string;
|
||||
kind: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityManifest {
|
||||
schemaVersion: number;
|
||||
generatedAt: string;
|
||||
source: {
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
} | null;
|
||||
includes: CompanyPortabilityInclude;
|
||||
company: CompanyPortabilityCompanyManifestEntry | null;
|
||||
sidebar: CompanyPortabilitySidebarOrder | null;
|
||||
agents: CompanyPortabilityAgentManifestEntry[];
|
||||
skills: CompanyPortabilitySkillManifestEntry[];
|
||||
projects: CompanyPortabilityProjectManifestEntry[];
|
||||
issues: CompanyPortabilityIssueManifestEntry[];
|
||||
envInputs: CompanyPortabilityEnvInput[];
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityExportResult {
|
||||
rootPath: string;
|
||||
manifest: CompanyPortabilityManifest;
|
||||
files: Record<string, CompanyPortabilityFileEntry>;
|
||||
warnings: string[];
|
||||
paperclipExtensionPath: string;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityExportPreviewFile {
|
||||
path: string;
|
||||
kind: "company" | "agent" | "skill" | "project" | "issue" | "extension" | "readme" | "other";
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityExportPreviewResult {
|
||||
rootPath: string;
|
||||
manifest: CompanyPortabilityManifest;
|
||||
files: Record<string, CompanyPortabilityFileEntry>;
|
||||
fileInventory: CompanyPortabilityExportPreviewFile[];
|
||||
counts: {
|
||||
files: number;
|
||||
agents: number;
|
||||
skills: number;
|
||||
projects: number;
|
||||
issues: number;
|
||||
};
|
||||
warnings: string[];
|
||||
paperclipExtensionPath: string;
|
||||
}
|
||||
|
||||
export type CompanyPortabilitySource =
|
||||
| {
|
||||
type: "inline";
|
||||
rootPath?: string | null;
|
||||
files: Record<string, CompanyPortabilityFileEntry>;
|
||||
}
|
||||
| {
|
||||
type: "github";
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type CompanyPortabilityImportTarget =
|
||||
| {
|
||||
mode: "new_company";
|
||||
newCompanyName?: string | null;
|
||||
}
|
||||
| {
|
||||
mode: "existing_company";
|
||||
companyId: string;
|
||||
};
|
||||
|
||||
export type CompanyPortabilityAgentSelection = "all" | string[];
|
||||
|
||||
export type CompanyPortabilityCollisionStrategy = "rename" | "skip" | "replace";
|
||||
|
||||
export interface CompanyPortabilityPreviewRequest {
|
||||
source: CompanyPortabilitySource;
|
||||
include?: Partial<CompanyPortabilityInclude>;
|
||||
target: CompanyPortabilityImportTarget;
|
||||
agents?: CompanyPortabilityAgentSelection;
|
||||
collisionStrategy?: CompanyPortabilityCollisionStrategy;
|
||||
nameOverrides?: Record<string, string>;
|
||||
selectedFiles?: string[];
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityPreviewAgentPlan {
|
||||
slug: string;
|
||||
action: "create" | "update" | "skip";
|
||||
plannedName: string;
|
||||
existingAgentId: string | null;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityPreviewProjectPlan {
|
||||
slug: string;
|
||||
action: "create" | "update" | "skip";
|
||||
plannedName: string;
|
||||
existingProjectId: string | null;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityPreviewIssuePlan {
|
||||
slug: string;
|
||||
action: "create" | "skip";
|
||||
plannedTitle: string;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityPreviewResult {
|
||||
include: CompanyPortabilityInclude;
|
||||
targetCompanyId: string | null;
|
||||
targetCompanyName: string | null;
|
||||
collisionStrategy: CompanyPortabilityCollisionStrategy;
|
||||
selectedAgentSlugs: string[];
|
||||
plan: {
|
||||
companyAction: "none" | "create" | "update";
|
||||
agentPlans: CompanyPortabilityPreviewAgentPlan[];
|
||||
projectPlans: CompanyPortabilityPreviewProjectPlan[];
|
||||
issuePlans: CompanyPortabilityPreviewIssuePlan[];
|
||||
};
|
||||
manifest: CompanyPortabilityManifest;
|
||||
files: Record<string, CompanyPortabilityFileEntry>;
|
||||
envInputs: CompanyPortabilityEnvInput[];
|
||||
warnings: string[];
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityAdapterOverride {
|
||||
adapterType: string;
|
||||
adapterConfig?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityImportRequest extends CompanyPortabilityPreviewRequest {
|
||||
adapterOverrides?: Record<string, CompanyPortabilityAdapterOverride>;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityImportResult {
|
||||
company: {
|
||||
id: string;
|
||||
name: string;
|
||||
action: "created" | "updated" | "unchanged";
|
||||
};
|
||||
agents: {
|
||||
slug: string;
|
||||
id: string | null;
|
||||
action: "created" | "updated" | "skipped";
|
||||
name: string;
|
||||
reason: string | null;
|
||||
}[];
|
||||
projects: {
|
||||
slug: string;
|
||||
id: string | null;
|
||||
action: "created" | "updated" | "skipped";
|
||||
name: string;
|
||||
reason: string | null;
|
||||
}[];
|
||||
envInputs: CompanyPortabilityEnvInput[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityExportRequest {
|
||||
include?: Partial<CompanyPortabilityInclude>;
|
||||
agents?: string[];
|
||||
skills?: string[];
|
||||
projects?: string[];
|
||||
issues?: string[];
|
||||
projectIssues?: string[];
|
||||
selectedFiles?: string[];
|
||||
expandReferencedSkills?: boolean;
|
||||
sidebarOrder?: Partial<CompanyPortabilitySidebarOrder>;
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
export type CompanySkillSourceType = "local_path" | "github" | "url" | "catalog" | "skills_sh";
|
||||
|
||||
export type CompanySkillTrustLevel = "markdown_only" | "assets" | "scripts_executables";
|
||||
|
||||
export type CompanySkillCompatibility = "compatible" | "unknown" | "invalid";
|
||||
|
||||
export type CompanySkillSourceBadge = "paperclip" | "github" | "local" | "url" | "catalog" | "skills_sh";
|
||||
|
||||
export interface CompanySkillFileInventoryEntry {
|
||||
path: string;
|
||||
kind: "skill" | "markdown" | "reference" | "script" | "asset" | "other";
|
||||
}
|
||||
|
||||
export interface CompanySkill {
|
||||
id: string;
|
||||
companyId: string;
|
||||
key: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
markdown: string;
|
||||
sourceType: CompanySkillSourceType;
|
||||
sourceLocator: string | null;
|
||||
sourceRef: string | null;
|
||||
trustLevel: CompanySkillTrustLevel;
|
||||
compatibility: CompanySkillCompatibility;
|
||||
fileInventory: CompanySkillFileInventoryEntry[];
|
||||
metadata: Record<string, unknown> | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface CompanySkillListItem {
|
||||
id: string;
|
||||
companyId: string;
|
||||
key: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
sourceType: CompanySkillSourceType;
|
||||
sourceLocator: string | null;
|
||||
sourceRef: string | null;
|
||||
trustLevel: CompanySkillTrustLevel;
|
||||
compatibility: CompanySkillCompatibility;
|
||||
fileInventory: CompanySkillFileInventoryEntry[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
attachedAgentCount: number;
|
||||
editable: boolean;
|
||||
editableReason: string | null;
|
||||
sourceLabel: string | null;
|
||||
sourceBadge: CompanySkillSourceBadge;
|
||||
sourcePath: string | null;
|
||||
}
|
||||
|
||||
export interface CompanySkillUsageAgent {
|
||||
id: string;
|
||||
name: string;
|
||||
urlKey: string;
|
||||
adapterType: string;
|
||||
desired: boolean;
|
||||
actualState: string | null;
|
||||
}
|
||||
|
||||
export interface CompanySkillDetail extends CompanySkill {
|
||||
attachedAgentCount: number;
|
||||
usedByAgents: CompanySkillUsageAgent[];
|
||||
editable: boolean;
|
||||
editableReason: string | null;
|
||||
sourceLabel: string | null;
|
||||
sourceBadge: CompanySkillSourceBadge;
|
||||
sourcePath: string | null;
|
||||
}
|
||||
|
||||
export interface CompanySkillUpdateStatus {
|
||||
supported: boolean;
|
||||
reason: string | null;
|
||||
trackingRef: string | null;
|
||||
currentRef: string | null;
|
||||
latestRef: string | null;
|
||||
hasUpdate: boolean;
|
||||
}
|
||||
|
||||
export interface CompanySkillImportRequest {
|
||||
source: string;
|
||||
}
|
||||
|
||||
export interface CompanySkillImportResult {
|
||||
imported: CompanySkill[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface CompanySkillProjectScanRequest {
|
||||
projectIds?: string[];
|
||||
workspaceIds?: string[];
|
||||
}
|
||||
|
||||
export interface CompanySkillProjectScanSkipped {
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
workspaceId: string | null;
|
||||
workspaceName: string | null;
|
||||
path: string | null;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface CompanySkillProjectScanConflict {
|
||||
slug: string;
|
||||
key: string;
|
||||
projectId: string;
|
||||
projectName: string;
|
||||
workspaceId: string;
|
||||
workspaceName: string;
|
||||
path: string;
|
||||
existingSkillId: string;
|
||||
existingSkillKey: string;
|
||||
existingSourceLocator: string | null;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface CompanySkillProjectScanResult {
|
||||
scannedProjects: number;
|
||||
scannedWorkspaces: number;
|
||||
discovered: number;
|
||||
imported: CompanySkill[];
|
||||
updated: CompanySkill[];
|
||||
skipped: CompanySkillProjectScanSkipped[];
|
||||
conflicts: CompanySkillProjectScanConflict[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface CompanySkillCreateRequest {
|
||||
name: string;
|
||||
slug?: string | null;
|
||||
description?: string | null;
|
||||
markdown?: string | null;
|
||||
}
|
||||
|
||||
export interface CompanySkillFileDetail {
|
||||
skillId: string;
|
||||
path: string;
|
||||
kind: CompanySkillFileInventoryEntry["kind"];
|
||||
content: string;
|
||||
language: string | null;
|
||||
markdown: boolean;
|
||||
editable: boolean;
|
||||
}
|
||||
|
||||
export interface CompanySkillFileUpdateRequest {
|
||||
path: string;
|
||||
content: string;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { CompanyStatus, PauseReason } from "../constants.js";
|
||||
|
||||
export interface Company {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
status: CompanyStatus;
|
||||
pauseReason: PauseReason | null;
|
||||
pausedAt: Date | null;
|
||||
issuePrefix: string;
|
||||
issueCounter: number;
|
||||
budgetMonthlyCents: number;
|
||||
spentMonthlyCents: number;
|
||||
requireBoardApprovalForNewAgents: boolean;
|
||||
brandColor: string | null;
|
||||
logoAssetId: string | null;
|
||||
logoUrl: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import type { BillingType } from "../constants.js";
|
||||
|
||||
export interface CostEvent {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string;
|
||||
issueId: string | null;
|
||||
projectId: string | null;
|
||||
goalId: string | null;
|
||||
heartbeatRunId: string | null;
|
||||
billingCode: string | null;
|
||||
provider: string;
|
||||
biller: string;
|
||||
billingType: BillingType;
|
||||
model: string;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
costCents: number;
|
||||
occurredAt: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface CostSummary {
|
||||
companyId: string;
|
||||
spendCents: number;
|
||||
budgetCents: number;
|
||||
utilizationPercent: number;
|
||||
}
|
||||
|
||||
export interface CostByAgent {
|
||||
agentId: string;
|
||||
agentName: string | null;
|
||||
agentStatus: string | null;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
apiRunCount: number;
|
||||
subscriptionRunCount: number;
|
||||
subscriptionCachedInputTokens: number;
|
||||
subscriptionInputTokens: number;
|
||||
subscriptionOutputTokens: number;
|
||||
}
|
||||
|
||||
export interface CostByProviderModel {
|
||||
provider: string;
|
||||
biller: string;
|
||||
billingType: BillingType;
|
||||
model: string;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
apiRunCount: number;
|
||||
subscriptionRunCount: number;
|
||||
subscriptionCachedInputTokens: number;
|
||||
subscriptionInputTokens: number;
|
||||
subscriptionOutputTokens: number;
|
||||
}
|
||||
|
||||
export interface CostByBiller {
|
||||
biller: string;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
apiRunCount: number;
|
||||
subscriptionRunCount: number;
|
||||
subscriptionCachedInputTokens: number;
|
||||
subscriptionInputTokens: number;
|
||||
subscriptionOutputTokens: number;
|
||||
providerCount: number;
|
||||
modelCount: number;
|
||||
}
|
||||
|
||||
/** per-agent breakdown by provider + model, for identifying token-hungry agents */
|
||||
export interface CostByAgentModel {
|
||||
agentId: string;
|
||||
agentName: string | null;
|
||||
provider: string;
|
||||
biller: string;
|
||||
billingType: BillingType;
|
||||
model: string;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
}
|
||||
|
||||
/** spend per provider for a fixed rolling time window */
|
||||
export interface CostWindowSpendRow {
|
||||
provider: string;
|
||||
biller: string;
|
||||
/** duration label, e.g. "5h", "24h", "7d" */
|
||||
window: string;
|
||||
/** rolling window duration in hours */
|
||||
windowHours: number;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
}
|
||||
|
||||
/** cost attributed to a project via heartbeat run → activity log → issue → project chain */
|
||||
export interface CostByProject {
|
||||
projectId: string | null;
|
||||
projectName: string | null;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
cachedInputTokens: number;
|
||||
outputTokens: number;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
export interface DashboardSummary {
|
||||
companyId: string;
|
||||
agents: {
|
||||
active: number;
|
||||
running: number;
|
||||
paused: number;
|
||||
error: number;
|
||||
};
|
||||
tasks: {
|
||||
open: number;
|
||||
inProgress: number;
|
||||
blocked: number;
|
||||
done: number;
|
||||
};
|
||||
costs: {
|
||||
monthSpendCents: number;
|
||||
monthBudgetCents: number;
|
||||
monthUtilizationPercent: number;
|
||||
};
|
||||
pendingApprovals: number;
|
||||
budgets: {
|
||||
activeIncidents: number;
|
||||
pendingApprovals: number;
|
||||
pausedAgents: number;
|
||||
pausedProjects: number;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { AgentAdapterType, FinanceDirection, FinanceEventKind, FinanceUnit } from "../constants.js";
|
||||
|
||||
export interface FinanceEvent {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string | null;
|
||||
issueId: string | null;
|
||||
projectId: string | null;
|
||||
goalId: string | null;
|
||||
heartbeatRunId: string | null;
|
||||
costEventId: string | null;
|
||||
billingCode: string | null;
|
||||
description: string | null;
|
||||
eventKind: FinanceEventKind;
|
||||
direction: FinanceDirection;
|
||||
biller: string;
|
||||
provider: string | null;
|
||||
executionAdapterType: AgentAdapterType | null;
|
||||
pricingTier: string | null;
|
||||
region: string | null;
|
||||
model: string | null;
|
||||
quantity: number | null;
|
||||
unit: FinanceUnit | null;
|
||||
amountCents: number;
|
||||
currency: string;
|
||||
estimated: boolean;
|
||||
externalInvoiceId: string | null;
|
||||
metadataJson: Record<string, unknown> | null;
|
||||
occurredAt: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface FinanceSummary {
|
||||
companyId: string;
|
||||
debitCents: number;
|
||||
creditCents: number;
|
||||
netCents: number;
|
||||
estimatedDebitCents: number;
|
||||
eventCount: number;
|
||||
}
|
||||
|
||||
export interface FinanceByBiller {
|
||||
biller: string;
|
||||
debitCents: number;
|
||||
creditCents: number;
|
||||
netCents: number;
|
||||
estimatedDebitCents: number;
|
||||
eventCount: number;
|
||||
kindCount: number;
|
||||
}
|
||||
|
||||
export interface FinanceByKind {
|
||||
eventKind: FinanceEventKind;
|
||||
debitCents: number;
|
||||
creditCents: number;
|
||||
netCents: number;
|
||||
estimatedDebitCents: number;
|
||||
eventCount: number;
|
||||
billerCount: number;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { GoalLevel, GoalStatus } from "../constants.js";
|
||||
|
||||
export interface Goal {
|
||||
id: string;
|
||||
companyId: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
level: GoalLevel;
|
||||
status: GoalStatus;
|
||||
parentId: string | null;
|
||||
ownerAgentId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import type {
|
||||
AgentRole,
|
||||
AgentStatus,
|
||||
HeartbeatInvocationSource,
|
||||
HeartbeatRunStatus,
|
||||
WakeupTriggerDetail,
|
||||
WakeupRequestStatus,
|
||||
} from "../constants.js";
|
||||
|
||||
export interface HeartbeatRun {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string;
|
||||
invocationSource: HeartbeatInvocationSource;
|
||||
triggerDetail: WakeupTriggerDetail | null;
|
||||
status: HeartbeatRunStatus;
|
||||
startedAt: Date | null;
|
||||
finishedAt: Date | null;
|
||||
error: string | null;
|
||||
wakeupRequestId: string | null;
|
||||
exitCode: number | null;
|
||||
signal: string | null;
|
||||
usageJson: Record<string, unknown> | null;
|
||||
resultJson: Record<string, unknown> | null;
|
||||
sessionIdBefore: string | null;
|
||||
sessionIdAfter: string | null;
|
||||
logStore: string | null;
|
||||
logRef: string | null;
|
||||
logBytes: number | null;
|
||||
logSha256: string | null;
|
||||
logCompressed: boolean;
|
||||
stdoutExcerpt: string | null;
|
||||
stderrExcerpt: string | null;
|
||||
errorCode: string | null;
|
||||
externalRunId: string | null;
|
||||
processPid: number | null;
|
||||
processStartedAt: Date | null;
|
||||
retryOfRunId: string | null;
|
||||
processLossRetryCount: number;
|
||||
contextSnapshot: Record<string, unknown> | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface HeartbeatRunEvent {
|
||||
id: number;
|
||||
companyId: string;
|
||||
runId: string;
|
||||
agentId: string;
|
||||
seq: number;
|
||||
eventType: string;
|
||||
stream: "system" | "stdout" | "stderr" | null;
|
||||
level: "info" | "warn" | "error" | null;
|
||||
color: string | null;
|
||||
message: string | null;
|
||||
payload: Record<string, unknown> | null;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface AgentRuntimeState {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
adapterType: string;
|
||||
sessionId: string | null;
|
||||
sessionDisplayId?: string | null;
|
||||
sessionParamsJson?: Record<string, unknown> | null;
|
||||
stateJson: Record<string, unknown>;
|
||||
lastRunId: string | null;
|
||||
lastRunStatus: string | null;
|
||||
totalInputTokens: number;
|
||||
totalOutputTokens: number;
|
||||
totalCachedInputTokens: number;
|
||||
totalCostCents: number;
|
||||
lastError: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface AgentTaskSession {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string;
|
||||
adapterType: string;
|
||||
taskKey: string;
|
||||
sessionParamsJson: Record<string, unknown> | null;
|
||||
sessionDisplayId: string | null;
|
||||
lastRunId: string | null;
|
||||
lastError: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface AgentWakeupRequest {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string;
|
||||
source: HeartbeatInvocationSource;
|
||||
triggerDetail: WakeupTriggerDetail | null;
|
||||
reason: string | null;
|
||||
payload: Record<string, unknown> | null;
|
||||
status: WakeupRequestStatus;
|
||||
coalescedCount: number;
|
||||
requestedByActorType: "user" | "agent" | "system" | null;
|
||||
requestedByActorId: string | null;
|
||||
idempotencyKey: string | null;
|
||||
runId: string | null;
|
||||
requestedAt: Date;
|
||||
claimedAt: Date | null;
|
||||
finishedAt: Date | null;
|
||||
error: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface InstanceSchedulerHeartbeatAgent {
|
||||
id: string;
|
||||
companyId: string;
|
||||
companyName: string;
|
||||
companyIssuePrefix: string;
|
||||
agentName: string;
|
||||
agentUrlKey: string;
|
||||
role: AgentRole;
|
||||
title: string | null;
|
||||
status: AgentStatus;
|
||||
adapterType: string;
|
||||
intervalSec: number;
|
||||
heartbeatEnabled: boolean;
|
||||
schedulerActive: boolean;
|
||||
lastHeartbeatAt: Date | null;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
export type { Company } from "./company.js";
|
||||
export type { InstanceExperimentalSettings, InstanceGeneralSettings, InstanceSettings } from "./instance.js";
|
||||
export type {
|
||||
CompanySkillSourceType,
|
||||
CompanySkillTrustLevel,
|
||||
CompanySkillCompatibility,
|
||||
CompanySkillSourceBadge,
|
||||
CompanySkillFileInventoryEntry,
|
||||
CompanySkill,
|
||||
CompanySkillListItem,
|
||||
CompanySkillUsageAgent,
|
||||
CompanySkillDetail,
|
||||
CompanySkillUpdateStatus,
|
||||
CompanySkillImportRequest,
|
||||
CompanySkillImportResult,
|
||||
CompanySkillProjectScanRequest,
|
||||
CompanySkillProjectScanSkipped,
|
||||
CompanySkillProjectScanConflict,
|
||||
CompanySkillProjectScanResult,
|
||||
CompanySkillCreateRequest,
|
||||
CompanySkillFileDetail,
|
||||
CompanySkillFileUpdateRequest,
|
||||
} from "./company-skill.js";
|
||||
export type {
|
||||
AgentSkillSyncMode,
|
||||
AgentSkillState,
|
||||
AgentSkillOrigin,
|
||||
AgentSkillEntry,
|
||||
AgentSkillSnapshot,
|
||||
AgentSkillSyncRequest,
|
||||
} from "./adapter-skills.js";
|
||||
export type {
|
||||
Agent,
|
||||
AgentAccessState,
|
||||
AgentChainOfCommandEntry,
|
||||
AgentDetail,
|
||||
AgentPermissions,
|
||||
AgentInstructionsBundleMode,
|
||||
AgentInstructionsFileSummary,
|
||||
AgentInstructionsFileDetail,
|
||||
AgentInstructionsBundle,
|
||||
AgentKeyCreated,
|
||||
AgentConfigRevision,
|
||||
AdapterEnvironmentCheckLevel,
|
||||
AdapterEnvironmentTestStatus,
|
||||
AdapterEnvironmentCheck,
|
||||
AdapterEnvironmentTestResult,
|
||||
} from "./agent.js";
|
||||
export type { AssetImage } from "./asset.js";
|
||||
export type { Project, ProjectCodebase, ProjectCodebaseOrigin, ProjectGoalRef, ProjectWorkspace } from "./project.js";
|
||||
export type {
|
||||
ExecutionWorkspace,
|
||||
WorkspaceRuntimeService,
|
||||
ExecutionWorkspaceStrategyType,
|
||||
ExecutionWorkspaceMode,
|
||||
ExecutionWorkspaceProviderType,
|
||||
ExecutionWorkspaceStatus,
|
||||
ExecutionWorkspaceStrategy,
|
||||
ProjectExecutionWorkspacePolicy,
|
||||
ProjectExecutionWorkspaceDefaultMode,
|
||||
IssueExecutionWorkspaceSettings,
|
||||
} from "./workspace-runtime.js";
|
||||
export type {
|
||||
WorkspaceOperation,
|
||||
WorkspaceOperationPhase,
|
||||
WorkspaceOperationStatus,
|
||||
} from "./workspace-operation.js";
|
||||
export type {
|
||||
IssueWorkProduct,
|
||||
IssueWorkProductType,
|
||||
IssueWorkProductProvider,
|
||||
IssueWorkProductStatus,
|
||||
IssueWorkProductReviewState,
|
||||
} from "./work-product.js";
|
||||
export type {
|
||||
Issue,
|
||||
IssueAssigneeAdapterOverrides,
|
||||
IssueComment,
|
||||
IssueDocument,
|
||||
IssueDocumentSummary,
|
||||
DocumentRevision,
|
||||
DocumentFormat,
|
||||
LegacyPlanDocument,
|
||||
IssueAncestor,
|
||||
IssueAncestorProject,
|
||||
IssueAncestorGoal,
|
||||
IssueAttachment,
|
||||
IssueLabel,
|
||||
} from "./issue.js";
|
||||
export type { Goal } from "./goal.js";
|
||||
export type { Approval, ApprovalComment } from "./approval.js";
|
||||
export type {
|
||||
BudgetPolicy,
|
||||
BudgetPolicySummary,
|
||||
BudgetIncident,
|
||||
BudgetOverview,
|
||||
BudgetPolicyUpsertInput,
|
||||
BudgetIncidentResolutionInput,
|
||||
} from "./budget.js";
|
||||
export type {
|
||||
SecretProvider,
|
||||
SecretVersionSelector,
|
||||
EnvPlainBinding,
|
||||
EnvSecretRefBinding,
|
||||
EnvBinding,
|
||||
AgentEnvConfig,
|
||||
CompanySecret,
|
||||
SecretProviderDescriptor,
|
||||
} from "./secrets.js";
|
||||
export type {
|
||||
Routine,
|
||||
RoutineTrigger,
|
||||
RoutineRun,
|
||||
RoutineTriggerSecretMaterial,
|
||||
RoutineDetail,
|
||||
RoutineRunSummary,
|
||||
RoutineExecutionIssueOrigin,
|
||||
RoutineListItem,
|
||||
} from "./routine.js";
|
||||
export type { CostEvent, CostSummary, CostByAgent, CostByProviderModel, CostByBiller, CostByAgentModel, CostWindowSpendRow, CostByProject } from "./cost.js";
|
||||
export type { FinanceEvent, FinanceSummary, FinanceByBiller, FinanceByKind } from "./finance.js";
|
||||
export type {
|
||||
HeartbeatRun,
|
||||
HeartbeatRunEvent,
|
||||
AgentRuntimeState,
|
||||
AgentTaskSession,
|
||||
AgentWakeupRequest,
|
||||
InstanceSchedulerHeartbeatAgent,
|
||||
} from "./heartbeat.js";
|
||||
export type { LiveEvent } from "./live.js";
|
||||
export type { DashboardSummary } from "./dashboard.js";
|
||||
export type { ActivityEvent } from "./activity.js";
|
||||
export type { SidebarBadges } from "./sidebar-badges.js";
|
||||
export type {
|
||||
CompanyMembership,
|
||||
PrincipalPermissionGrant,
|
||||
Invite,
|
||||
JoinRequest,
|
||||
InstanceUserRoleGrant,
|
||||
} from "./access.js";
|
||||
export type { QuotaWindow, ProviderQuotaResult } from "./quota.js";
|
||||
export type {
|
||||
CompanyPortabilityInclude,
|
||||
CompanyPortabilityEnvInput,
|
||||
CompanyPortabilityFileEntry,
|
||||
CompanyPortabilityCompanyManifestEntry,
|
||||
CompanyPortabilitySidebarOrder,
|
||||
CompanyPortabilityAgentManifestEntry,
|
||||
CompanyPortabilitySkillManifestEntry,
|
||||
CompanyPortabilityProjectManifestEntry,
|
||||
CompanyPortabilityProjectWorkspaceManifestEntry,
|
||||
CompanyPortabilityIssueRoutineTriggerManifestEntry,
|
||||
CompanyPortabilityIssueRoutineManifestEntry,
|
||||
CompanyPortabilityIssueManifestEntry,
|
||||
CompanyPortabilityManifest,
|
||||
CompanyPortabilityExportResult,
|
||||
CompanyPortabilityExportPreviewFile,
|
||||
CompanyPortabilityExportPreviewResult,
|
||||
CompanyPortabilitySource,
|
||||
CompanyPortabilityImportTarget,
|
||||
CompanyPortabilityAgentSelection,
|
||||
CompanyPortabilityCollisionStrategy,
|
||||
CompanyPortabilityPreviewRequest,
|
||||
CompanyPortabilityPreviewAgentPlan,
|
||||
CompanyPortabilityPreviewProjectPlan,
|
||||
CompanyPortabilityPreviewIssuePlan,
|
||||
CompanyPortabilityPreviewResult,
|
||||
CompanyPortabilityAdapterOverride,
|
||||
CompanyPortabilityImportRequest,
|
||||
CompanyPortabilityImportResult,
|
||||
CompanyPortabilityExportRequest,
|
||||
} from "./company-portability.js";
|
||||
export type {
|
||||
JsonSchema,
|
||||
PluginJobDeclaration,
|
||||
PluginWebhookDeclaration,
|
||||
PluginToolDeclaration,
|
||||
PluginUiSlotDeclaration,
|
||||
PluginLauncherActionDeclaration,
|
||||
PluginLauncherRenderDeclaration,
|
||||
PluginLauncherRenderContextSnapshot,
|
||||
PluginLauncherDeclaration,
|
||||
PluginMinimumHostVersion,
|
||||
PluginUiDeclaration,
|
||||
PaperclipPluginManifestV1,
|
||||
PluginRecord,
|
||||
PluginStateRecord,
|
||||
PluginConfig,
|
||||
PluginEntityRecord,
|
||||
PluginEntityQuery,
|
||||
PluginJobRecord,
|
||||
PluginJobRunRecord,
|
||||
PluginWebhookDeliveryRecord,
|
||||
} from "./plugin.js";
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface InstanceGeneralSettings {
|
||||
censorUsernameInLogs: boolean;
|
||||
}
|
||||
|
||||
export interface InstanceExperimentalSettings {
|
||||
enableIsolatedWorkspaces: boolean;
|
||||
autoRestartDevServerWhenIdle: boolean;
|
||||
}
|
||||
|
||||
export interface InstanceSettings {
|
||||
id: string;
|
||||
general: InstanceGeneralSettings;
|
||||
experimental: InstanceExperimentalSettings;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
import type { IssueOriginKind, IssuePriority, IssueStatus } from "../constants.js";
|
||||
import type { Goal } from "./goal.js";
|
||||
import type { Project, ProjectWorkspace } from "./project.js";
|
||||
import type { ExecutionWorkspace, IssueExecutionWorkspaceSettings } from "./workspace-runtime.js";
|
||||
import type { IssueWorkProduct } from "./work-product.js";
|
||||
|
||||
export interface IssueAncestorProject {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
status: string;
|
||||
goalId: string | null;
|
||||
workspaces: ProjectWorkspace[];
|
||||
primaryWorkspace: ProjectWorkspace | null;
|
||||
}
|
||||
|
||||
export interface IssueAncestorGoal {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
level: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface IssueAncestor {
|
||||
id: string;
|
||||
identifier: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
status: string;
|
||||
priority: string;
|
||||
assigneeAgentId: string | null;
|
||||
assigneeUserId: string | null;
|
||||
projectId: string | null;
|
||||
goalId: string | null;
|
||||
project: IssueAncestorProject | null;
|
||||
goal: IssueAncestorGoal | null;
|
||||
}
|
||||
|
||||
export interface IssueLabel {
|
||||
id: string;
|
||||
companyId: string;
|
||||
name: string;
|
||||
color: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface IssueAssigneeAdapterOverrides {
|
||||
adapterConfig?: Record<string, unknown>;
|
||||
useProjectWorkspace?: boolean;
|
||||
}
|
||||
|
||||
export type DocumentFormat = "markdown";
|
||||
|
||||
export interface IssueDocumentSummary {
|
||||
id: string;
|
||||
companyId: string;
|
||||
issueId: string;
|
||||
key: string;
|
||||
title: string | null;
|
||||
format: DocumentFormat;
|
||||
latestRevisionId: string | null;
|
||||
latestRevisionNumber: number;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
updatedByAgentId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface IssueDocument extends IssueDocumentSummary {
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface DocumentRevision {
|
||||
id: string;
|
||||
companyId: string;
|
||||
documentId: string;
|
||||
issueId: string;
|
||||
key: string;
|
||||
revisionNumber: number;
|
||||
body: string;
|
||||
changeSummary: string | null;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface LegacyPlanDocument {
|
||||
key: "plan";
|
||||
body: string;
|
||||
source: "issue_description";
|
||||
}
|
||||
|
||||
export interface Issue {
|
||||
id: string;
|
||||
companyId: string;
|
||||
projectId: string | null;
|
||||
projectWorkspaceId: string | null;
|
||||
goalId: string | null;
|
||||
parentId: string | null;
|
||||
ancestors?: IssueAncestor[];
|
||||
title: string;
|
||||
description: string | null;
|
||||
status: IssueStatus;
|
||||
priority: IssuePriority;
|
||||
assigneeAgentId: string | null;
|
||||
assigneeUserId: string | null;
|
||||
checkoutRunId: string | null;
|
||||
executionRunId: string | null;
|
||||
executionAgentNameKey: string | null;
|
||||
executionLockedAt: Date | null;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
issueNumber: number | null;
|
||||
identifier: string | null;
|
||||
originKind?: IssueOriginKind;
|
||||
originId?: string | null;
|
||||
originRunId?: string | null;
|
||||
requestDepth: number;
|
||||
billingCode: string | null;
|
||||
assigneeAdapterOverrides: IssueAssigneeAdapterOverrides | null;
|
||||
executionWorkspaceId: string | null;
|
||||
executionWorkspacePreference: string | null;
|
||||
executionWorkspaceSettings: IssueExecutionWorkspaceSettings | null;
|
||||
startedAt: Date | null;
|
||||
completedAt: Date | null;
|
||||
cancelledAt: Date | null;
|
||||
hiddenAt: Date | null;
|
||||
labelIds?: string[];
|
||||
labels?: IssueLabel[];
|
||||
planDocument?: IssueDocument | null;
|
||||
documentSummaries?: IssueDocumentSummary[];
|
||||
legacyPlanDocument?: LegacyPlanDocument | null;
|
||||
project?: Project | null;
|
||||
goal?: Goal | null;
|
||||
currentExecutionWorkspace?: ExecutionWorkspace | null;
|
||||
workProducts?: IssueWorkProduct[];
|
||||
mentionedProjects?: Project[];
|
||||
myLastTouchAt?: Date | null;
|
||||
lastExternalCommentAt?: Date | null;
|
||||
isUnreadForMe?: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface IssueComment {
|
||||
id: string;
|
||||
companyId: string;
|
||||
issueId: string;
|
||||
authorAgentId: string | null;
|
||||
authorUserId: string | null;
|
||||
body: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface IssueAttachment {
|
||||
id: string;
|
||||
companyId: string;
|
||||
issueId: string;
|
||||
issueCommentId: string | null;
|
||||
assetId: string;
|
||||
provider: string;
|
||||
objectKey: string;
|
||||
contentType: string;
|
||||
byteSize: number;
|
||||
sha256: string;
|
||||
originalFilename: string | null;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
contentPath: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { LiveEventType } from "../constants.js";
|
||||
|
||||
export interface LiveEvent {
|
||||
id: number;
|
||||
companyId: string;
|
||||
type: LiveEventType;
|
||||
createdAt: string;
|
||||
payload: Record<string, unknown>;
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
import type {
|
||||
PluginStatus,
|
||||
PluginCategory,
|
||||
PluginCapability,
|
||||
PluginUiSlotType,
|
||||
PluginUiSlotEntityType,
|
||||
PluginStateScopeKind,
|
||||
PluginLauncherPlacementZone,
|
||||
PluginLauncherAction,
|
||||
PluginLauncherBounds,
|
||||
PluginLauncherRenderEnvironment,
|
||||
} from "../constants.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JSON Schema placeholder – plugins declare config schemas as JSON Schema
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A JSON Schema object used for plugin config schemas and tool parameter schemas.
|
||||
* Plugins provide these as plain JSON Schema compatible objects.
|
||||
*/
|
||||
export type JsonSchema = Record<string, unknown>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Manifest sub-types — nested declarations within PaperclipPluginManifestV1
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Declares a scheduled job a plugin can run.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §17 — Scheduled Jobs
|
||||
*/
|
||||
export interface PluginJobDeclaration {
|
||||
/** Stable identifier for this job, unique within the plugin. */
|
||||
jobKey: string;
|
||||
/** Human-readable name shown in the operator UI. */
|
||||
displayName: string;
|
||||
/** Optional description of what the job does. */
|
||||
description?: string;
|
||||
/** Cron expression for the schedule (e.g. "star/15 star star star star" or "0 * * * *"). */
|
||||
schedule?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a webhook endpoint the plugin can receive.
|
||||
* Route: `POST /api/plugins/:pluginId/webhooks/:endpointKey`
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §18 — Webhooks
|
||||
*/
|
||||
export interface PluginWebhookDeclaration {
|
||||
/** Stable identifier for this endpoint, unique within the plugin. */
|
||||
endpointKey: string;
|
||||
/** Human-readable name shown in the operator UI. */
|
||||
displayName: string;
|
||||
/** Optional description of what this webhook handles. */
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares an agent tool contributed by the plugin. Tools are namespaced
|
||||
* by plugin ID at runtime (e.g. `linear:search-issues`).
|
||||
*
|
||||
* Requires the `agent.tools.register` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §11 — Agent Tools
|
||||
*/
|
||||
export interface PluginToolDeclaration {
|
||||
/** Tool name, unique within the plugin. Namespaced by plugin ID at runtime. */
|
||||
name: string;
|
||||
/** Human-readable name shown to agents and in the UI. */
|
||||
displayName: string;
|
||||
/** Description provided to the agent so it knows when to use this tool. */
|
||||
description: string;
|
||||
/** JSON Schema describing the tool's input parameters. */
|
||||
parametersSchema: JsonSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a UI extension slot the plugin fills with a React component.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
*/
|
||||
export interface PluginUiSlotDeclaration {
|
||||
/** The type of UI mount point (page, detailTab, taskDetailView, toolbarButton, etc.). */
|
||||
type: PluginUiSlotType;
|
||||
/** Unique slot identifier within the plugin. */
|
||||
id: string;
|
||||
/** Human-readable name shown in navigation or tab labels. */
|
||||
displayName: string;
|
||||
/** Which export name in the UI bundle provides this component. */
|
||||
exportName: string;
|
||||
/**
|
||||
* Entity targets for context-sensitive slots.
|
||||
* Required for `detailTab`, `taskDetailView`, and `contextMenuItem`.
|
||||
*/
|
||||
entityTypes?: PluginUiSlotEntityType[];
|
||||
/**
|
||||
* Optional company-scoped route segment for page slots.
|
||||
* Example: `kitchensink` becomes `/:companyPrefix/kitchensink`.
|
||||
*/
|
||||
routePath?: string;
|
||||
/**
|
||||
* Optional ordering hint within a slot surface. Lower numbers appear first.
|
||||
* Defaults to host-defined ordering if omitted.
|
||||
*/
|
||||
order?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the action triggered by a plugin launcher surface.
|
||||
*/
|
||||
export interface PluginLauncherActionDeclaration {
|
||||
/** What kind of launch behavior the host should perform. */
|
||||
type: PluginLauncherAction;
|
||||
/**
|
||||
* Stable target identifier or URL. The meaning depends on `type`
|
||||
* (for example a route, tab key, action key, or external URL).
|
||||
*/
|
||||
target: string;
|
||||
/** Optional arbitrary parameters passed along to the target. */
|
||||
params?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional render metadata for the destination opened by a launcher.
|
||||
*/
|
||||
export interface PluginLauncherRenderDeclaration {
|
||||
/** High-level container the launcher expects the host to use. */
|
||||
environment: PluginLauncherRenderEnvironment;
|
||||
/** Optional size hint for the destination surface. */
|
||||
bounds?: PluginLauncherBounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializable runtime snapshot of the host launcher/container environment.
|
||||
*/
|
||||
export interface PluginLauncherRenderContextSnapshot {
|
||||
/** The current launcher/container environment selected by the host. */
|
||||
environment: PluginLauncherRenderEnvironment | null;
|
||||
/** Launcher id that opened this surface, if any. */
|
||||
launcherId: string | null;
|
||||
/** Current host-applied bounds hint for the environment, if any. */
|
||||
bounds: PluginLauncherBounds | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a plugin launcher surface independent of the low-level slot
|
||||
* implementation that mounts it.
|
||||
*/
|
||||
export interface PluginLauncherDeclaration {
|
||||
/** Stable identifier for this launcher, unique within the plugin. */
|
||||
id: string;
|
||||
/** Human-readable label shown for the launcher. */
|
||||
displayName: string;
|
||||
/** Optional description for operator-facing docs or future UI affordances. */
|
||||
description?: string;
|
||||
/** Where in the host UI this launcher should be placed. */
|
||||
placementZone: PluginLauncherPlacementZone;
|
||||
/** Optional export name in the UI bundle when the launcher has custom UI. */
|
||||
exportName?: string;
|
||||
/**
|
||||
* Optional entity targeting for context-sensitive launcher zones.
|
||||
* Reuses the same entity union as UI slots for consistency.
|
||||
*/
|
||||
entityTypes?: PluginUiSlotEntityType[];
|
||||
/** Optional ordering hint within the placement zone. */
|
||||
order?: number;
|
||||
/** What should happen when the launcher is activated. */
|
||||
action: PluginLauncherActionDeclaration;
|
||||
/** Optional render/container hints for the launched destination. */
|
||||
render?: PluginLauncherRenderDeclaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower-bound semver requirement for the Paperclip host.
|
||||
*
|
||||
* The host should reject installation when its running version is lower than
|
||||
* the declared minimum.
|
||||
*/
|
||||
export type PluginMinimumHostVersion = string;
|
||||
|
||||
/**
|
||||
* Groups plugin UI declarations that are served from the shared UI bundle
|
||||
* root declared in `entrypoints.ui`.
|
||||
*/
|
||||
export interface PluginUiDeclaration {
|
||||
/** UI extension slots this plugin fills. */
|
||||
slots?: PluginUiSlotDeclaration[];
|
||||
/** Declarative launcher metadata for host-mounted plugin entry points. */
|
||||
launchers?: PluginLauncherDeclaration[];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Manifest V1
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The manifest shape every plugin package must export.
|
||||
* See PLUGIN_SPEC.md §10.1 for the normative definition.
|
||||
*/
|
||||
export interface PaperclipPluginManifestV1 {
|
||||
/** Globally unique plugin identifier (e.g. `"acme.linear-sync"`). Must be lowercase alphanumeric with dots, hyphens, or underscores. */
|
||||
id: string;
|
||||
/** Plugin API version. Must be `1` for the current spec. */
|
||||
apiVersion: 1;
|
||||
/** Semver version of the plugin package (e.g. `"1.2.0"`). */
|
||||
version: string;
|
||||
/** Human-readable name (max 100 chars). */
|
||||
displayName: string;
|
||||
/** Short description (max 500 chars). */
|
||||
description: string;
|
||||
/** Author name (max 200 chars). May include email in angle brackets, e.g. `"Jane Doe <jane@example.com>"`. */
|
||||
author: string;
|
||||
/** One or more categories classifying this plugin. */
|
||||
categories: PluginCategory[];
|
||||
/**
|
||||
* Minimum host version required (semver lower bound).
|
||||
* Preferred generic field for new manifests.
|
||||
*/
|
||||
minimumHostVersion?: PluginMinimumHostVersion;
|
||||
/**
|
||||
* Legacy alias for `minimumHostVersion`.
|
||||
* Kept for backwards compatibility with existing manifests and docs.
|
||||
*/
|
||||
minimumPaperclipVersion?: PluginMinimumHostVersion;
|
||||
/** Capabilities this plugin requires from the host. Enforced at runtime. */
|
||||
capabilities: PluginCapability[];
|
||||
/** Entrypoint paths relative to the package root. */
|
||||
entrypoints: {
|
||||
/** Path to the worker entrypoint (required). */
|
||||
worker: string;
|
||||
/** Path to the UI bundle directory (required when `ui.slots` is declared). */
|
||||
ui?: string;
|
||||
};
|
||||
/** JSON Schema for operator-editable instance configuration. */
|
||||
instanceConfigSchema?: JsonSchema;
|
||||
/** Scheduled jobs this plugin declares. Requires `jobs.schedule` capability. */
|
||||
jobs?: PluginJobDeclaration[];
|
||||
/** Webhook endpoints this plugin declares. Requires `webhooks.receive` capability. */
|
||||
webhooks?: PluginWebhookDeclaration[];
|
||||
/** Agent tools this plugin contributes. Requires `agent.tools.register` capability. */
|
||||
tools?: PluginToolDeclaration[];
|
||||
/**
|
||||
* Legacy top-level launcher declarations.
|
||||
* Prefer `ui.launchers` for new manifests.
|
||||
*/
|
||||
launchers?: PluginLauncherDeclaration[];
|
||||
/** UI bundle declarations. Requires `entrypoints.ui` when populated. */
|
||||
ui?: PluginUiDeclaration;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Record – represents a row in the `plugins` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for an installed plugin as persisted in the `plugins` table.
|
||||
* See PLUGIN_SPEC.md §21.3 for the schema definition.
|
||||
*/
|
||||
export interface PluginRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** Unique key derived from `manifest.id`. Used for lookups. */
|
||||
pluginKey: string;
|
||||
/** npm package name (e.g. `"@acme/plugin-linear"`). */
|
||||
packageName: string;
|
||||
/** Installed semver version. */
|
||||
version: string;
|
||||
/** Plugin API version from the manifest. */
|
||||
apiVersion: number;
|
||||
/** Plugin categories from the manifest. */
|
||||
categories: PluginCategory[];
|
||||
/** Full manifest snapshot persisted at install/upgrade time. */
|
||||
manifestJson: PaperclipPluginManifestV1;
|
||||
/** Current lifecycle status. */
|
||||
status: PluginStatus;
|
||||
/** Deterministic load order (null if not yet assigned). */
|
||||
installOrder: number | null;
|
||||
/** Resolved package path for local-path installs; used to find worker entrypoint. */
|
||||
packagePath: string | null;
|
||||
/** Most recent error message, or operator-provided disable reason. */
|
||||
lastError: string | null;
|
||||
/** Timestamp when the plugin was first installed. */
|
||||
installedAt: Date;
|
||||
/** Timestamp of the most recent status or metadata change. */
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin State – represents a row in the `plugin_state` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for a single scoped key-value entry in the `plugin_state` table.
|
||||
* Plugins read and write these entries through `ctx.state` in the SDK.
|
||||
*
|
||||
* The five-part composite key `(pluginId, scopeKind, scopeId, namespace, stateKey)`
|
||||
* uniquely identifies a state entry.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 — `plugin_state`
|
||||
*/
|
||||
export interface PluginStateRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** FK to `plugins.id`. */
|
||||
pluginId: string;
|
||||
/** Granularity of the scope. */
|
||||
scopeKind: PluginStateScopeKind;
|
||||
/**
|
||||
* UUID or text identifier for the scoped object.
|
||||
* `null` for `instance` scope (no associated entity).
|
||||
*/
|
||||
scopeId: string | null;
|
||||
/**
|
||||
* Sub-namespace within the scope to avoid key collisions.
|
||||
* Defaults to `"default"` if not explicitly set by the plugin.
|
||||
*/
|
||||
namespace: string;
|
||||
/** The key for this state entry within the namespace. */
|
||||
stateKey: string;
|
||||
/** Stored JSON value. May be any JSON-serializable type. */
|
||||
valueJson: unknown;
|
||||
/** Timestamp of the most recent write. */
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Config – represents a row in the `plugin_config` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for a plugin's instance configuration as persisted in the
|
||||
* `plugin_config` table.
|
||||
* See PLUGIN_SPEC.md §21.3 for the schema definition.
|
||||
*/
|
||||
export interface PluginConfig {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** FK to `plugins.id`. Unique — each plugin has at most one config row. */
|
||||
pluginId: string;
|
||||
/** Operator-provided configuration values (validated against `instanceConfigSchema`). */
|
||||
configJson: Record<string, unknown>;
|
||||
/** Most recent config validation error, if any. */
|
||||
lastError: string | null;
|
||||
/** Timestamp when the config row was created. */
|
||||
createdAt: Date;
|
||||
/** Timestamp of the most recent config update. */
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query filter for `ctx.entities.list`.
|
||||
*/
|
||||
export interface PluginEntityQuery {
|
||||
/** Optional filter by entity type (e.g. 'project', 'issue'). */
|
||||
entityType?: string;
|
||||
/** Optional filter by external system identifier. */
|
||||
externalId?: string;
|
||||
/** Maximum number of records to return. Defaults to 100. */
|
||||
limit?: number;
|
||||
/** Number of records to skip. Defaults to 0. */
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Entity – represents a row in the `plugin_entities` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for an external entity mapping as persisted in the `plugin_entities` table.
|
||||
*/
|
||||
export interface PluginEntityRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** FK to `plugins.id`. */
|
||||
pluginId: string;
|
||||
/** Plugin-defined entity type. */
|
||||
entityType: string;
|
||||
/** Scope where this entity lives. */
|
||||
scopeKind: PluginStateScopeKind;
|
||||
/** UUID or text identifier for the scoped object. */
|
||||
scopeId: string | null;
|
||||
/** External identifier in the remote system. */
|
||||
externalId: string | null;
|
||||
/** Human-readable title. */
|
||||
title: string | null;
|
||||
/** Optional status string. */
|
||||
status: string | null;
|
||||
/** Full entity data blob. */
|
||||
data: Record<string, unknown>;
|
||||
/** ISO 8601 creation timestamp. */
|
||||
createdAt: Date;
|
||||
/** ISO 8601 last-updated timestamp. */
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Job – represents a row in the `plugin_jobs` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for a registered plugin job as persisted in the `plugin_jobs` table.
|
||||
*/
|
||||
export interface PluginJobRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** FK to `plugins.id`. */
|
||||
pluginId: string;
|
||||
/** Job key matching the manifest declaration. */
|
||||
jobKey: string;
|
||||
/** Cron expression for the schedule. */
|
||||
schedule: string;
|
||||
/** Current job status. */
|
||||
status: "active" | "paused" | "failed";
|
||||
/** Last time the job was executed. */
|
||||
lastRunAt: Date | null;
|
||||
/** Next scheduled execution time. */
|
||||
nextRunAt: Date | null;
|
||||
/** ISO 8601 creation timestamp. */
|
||||
createdAt: Date;
|
||||
/** ISO 8601 last-updated timestamp. */
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Job Run – represents a row in the `plugin_job_runs` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for a job execution history record.
|
||||
*/
|
||||
export interface PluginJobRunRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** FK to `plugin_jobs.id`. */
|
||||
jobId: string;
|
||||
/** FK to `plugins.id`. */
|
||||
pluginId: string;
|
||||
/** What triggered this run. */
|
||||
trigger: "schedule" | "manual" | "retry";
|
||||
/** Current run status. */
|
||||
status: "pending" | "queued" | "running" | "succeeded" | "failed" | "cancelled";
|
||||
/** Run duration in milliseconds. */
|
||||
durationMs: number | null;
|
||||
/** Error message if the run failed. */
|
||||
error: string | null;
|
||||
/** Run logs. */
|
||||
logs: string[];
|
||||
/** ISO 8601 start timestamp. */
|
||||
startedAt: Date | null;
|
||||
/** ISO 8601 finish timestamp. */
|
||||
finishedAt: Date | null;
|
||||
/** ISO 8601 creation timestamp. */
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Webhook Delivery – represents a row in the `plugin_webhook_deliveries` table
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Domain type for an inbound webhook delivery record.
|
||||
*/
|
||||
export interface PluginWebhookDeliveryRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** FK to `plugins.id`. */
|
||||
pluginId: string;
|
||||
/** Webhook endpoint key matching the manifest. */
|
||||
webhookKey: string;
|
||||
/** External identifier from the remote system. */
|
||||
externalId: string | null;
|
||||
/** Delivery status. */
|
||||
status: "pending" | "success" | "failed";
|
||||
/** Processing duration in milliseconds. */
|
||||
durationMs: number | null;
|
||||
/** Error message if processing failed. */
|
||||
error: string | null;
|
||||
/** Webhook payload. */
|
||||
payload: Record<string, unknown>;
|
||||
/** Webhook headers. */
|
||||
headers: Record<string, string>;
|
||||
/** ISO 8601 start timestamp. */
|
||||
startedAt: Date | null;
|
||||
/** ISO 8601 finish timestamp. */
|
||||
finishedAt: Date | null;
|
||||
/** ISO 8601 creation timestamp. */
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import type { PauseReason, ProjectStatus } from "../constants.js";
|
||||
import type { ProjectExecutionWorkspacePolicy, WorkspaceRuntimeService } from "./workspace-runtime.js";
|
||||
|
||||
export type ProjectWorkspaceSourceType = "local_path" | "git_repo" | "remote_managed" | "non_git_path";
|
||||
export type ProjectWorkspaceVisibility = "default" | "advanced";
|
||||
|
||||
export interface ProjectGoalRef {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface ProjectWorkspace {
|
||||
id: string;
|
||||
companyId: string;
|
||||
projectId: string;
|
||||
name: string;
|
||||
sourceType: ProjectWorkspaceSourceType;
|
||||
cwd: string | null;
|
||||
repoUrl: string | null;
|
||||
repoRef: string | null;
|
||||
defaultRef: string | null;
|
||||
visibility: ProjectWorkspaceVisibility;
|
||||
setupCommand: string | null;
|
||||
cleanupCommand: string | null;
|
||||
remoteProvider: string | null;
|
||||
remoteWorkspaceRef: string | null;
|
||||
sharedWorkspaceKey: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
isPrimary: boolean;
|
||||
runtimeServices?: WorkspaceRuntimeService[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export type ProjectCodebaseOrigin = "local_folder" | "managed_checkout";
|
||||
|
||||
export interface ProjectCodebase {
|
||||
workspaceId: string | null;
|
||||
repoUrl: string | null;
|
||||
repoRef: string | null;
|
||||
defaultRef: string | null;
|
||||
repoName: string | null;
|
||||
localFolder: string | null;
|
||||
managedFolder: string;
|
||||
effectiveLocalFolder: string;
|
||||
origin: ProjectCodebaseOrigin;
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: string;
|
||||
companyId: string;
|
||||
urlKey: string;
|
||||
/** @deprecated Use goalIds / goals instead */
|
||||
goalId: string | null;
|
||||
goalIds: string[];
|
||||
goals: ProjectGoalRef[];
|
||||
name: string;
|
||||
description: string | null;
|
||||
status: ProjectStatus;
|
||||
leadAgentId: string | null;
|
||||
targetDate: string | null;
|
||||
color: string | null;
|
||||
pauseReason: PauseReason | null;
|
||||
pausedAt: Date | null;
|
||||
executionWorkspacePolicy: ProjectExecutionWorkspacePolicy | null;
|
||||
codebase: ProjectCodebase;
|
||||
workspaces: ProjectWorkspace[];
|
||||
primaryWorkspace: ProjectWorkspace | null;
|
||||
archivedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/** a single rate-limit or usage window returned by a provider quota API */
|
||||
export interface QuotaWindow {
|
||||
/** human label, e.g. "5h", "7d", "Sonnet 7d", "Credits" */
|
||||
label: string;
|
||||
/** percent of the window already consumed (0-100), null when not reported */
|
||||
usedPercent: number | null;
|
||||
/** iso timestamp when this window resets, null when not reported */
|
||||
resetsAt: string | null;
|
||||
/** free-form value label for credit-style windows, e.g. "$4.20 remaining" */
|
||||
valueLabel: string | null;
|
||||
/** optional supporting text, e.g. reset details or provider-specific notes */
|
||||
detail?: string | null;
|
||||
}
|
||||
|
||||
/** result for one provider from the quota-windows endpoint */
|
||||
export interface ProviderQuotaResult {
|
||||
/** provider slug, e.g. "anthropic", "openai" */
|
||||
provider: string;
|
||||
/** source label when the provider reports where the quota data came from */
|
||||
source?: string | null;
|
||||
/** true when the fetch succeeded and windows is populated */
|
||||
ok: boolean;
|
||||
/** error message when ok is false */
|
||||
error?: string;
|
||||
windows: QuotaWindow[];
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import type { IssueOriginKind } from "../constants.js";
|
||||
|
||||
export interface RoutineProjectSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
status: string;
|
||||
goalId?: string | null;
|
||||
}
|
||||
|
||||
export interface RoutineAgentSummary {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
title: string | null;
|
||||
urlKey?: string | null;
|
||||
}
|
||||
|
||||
export interface RoutineIssueSummary {
|
||||
id: string;
|
||||
identifier: string | null;
|
||||
title: string;
|
||||
status: string;
|
||||
priority: string;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface Routine {
|
||||
id: string;
|
||||
companyId: string;
|
||||
projectId: string;
|
||||
goalId: string | null;
|
||||
parentIssueId: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
assigneeAgentId: string;
|
||||
priority: string;
|
||||
status: string;
|
||||
concurrencyPolicy: string;
|
||||
catchUpPolicy: string;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
updatedByAgentId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
lastTriggeredAt: Date | null;
|
||||
lastEnqueuedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface RoutineTrigger {
|
||||
id: string;
|
||||
companyId: string;
|
||||
routineId: string;
|
||||
kind: string;
|
||||
label: string | null;
|
||||
enabled: boolean;
|
||||
cronExpression: string | null;
|
||||
timezone: string | null;
|
||||
nextRunAt: Date | null;
|
||||
lastFiredAt: Date | null;
|
||||
publicId: string | null;
|
||||
secretId: string | null;
|
||||
signingMode: string | null;
|
||||
replayWindowSec: number | null;
|
||||
lastRotatedAt: Date | null;
|
||||
lastResult: string | null;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
updatedByAgentId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface RoutineRun {
|
||||
id: string;
|
||||
companyId: string;
|
||||
routineId: string;
|
||||
triggerId: string | null;
|
||||
source: string;
|
||||
status: string;
|
||||
triggeredAt: Date;
|
||||
idempotencyKey: string | null;
|
||||
triggerPayload: Record<string, unknown> | null;
|
||||
linkedIssueId: string | null;
|
||||
coalescedIntoRunId: string | null;
|
||||
failureReason: string | null;
|
||||
completedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface RoutineTriggerSecretMaterial {
|
||||
webhookUrl: string;
|
||||
webhookSecret: string;
|
||||
}
|
||||
|
||||
export interface RoutineDetail extends Routine {
|
||||
project: RoutineProjectSummary | null;
|
||||
assignee: RoutineAgentSummary | null;
|
||||
parentIssue: RoutineIssueSummary | null;
|
||||
triggers: RoutineTrigger[];
|
||||
recentRuns: RoutineRunSummary[];
|
||||
activeIssue: RoutineIssueSummary | null;
|
||||
}
|
||||
|
||||
export interface RoutineRunSummary extends RoutineRun {
|
||||
linkedIssue: RoutineIssueSummary | null;
|
||||
trigger: Pick<RoutineTrigger, "id" | "kind" | "label"> | null;
|
||||
}
|
||||
|
||||
export interface RoutineExecutionIssueOrigin {
|
||||
kind: Extract<IssueOriginKind, "routine_execution">;
|
||||
routineId: string;
|
||||
runId: string | null;
|
||||
}
|
||||
|
||||
export interface RoutineListItem extends Routine {
|
||||
triggers: Pick<RoutineTrigger, "id" | "kind" | "label" | "enabled" | "nextRunAt" | "lastFiredAt" | "lastResult">[];
|
||||
lastRun: RoutineRunSummary | null;
|
||||
activeIssue: RoutineIssueSummary | null;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
export type SecretProvider =
|
||||
| "local_encrypted"
|
||||
| "aws_secrets_manager"
|
||||
| "gcp_secret_manager"
|
||||
| "vault";
|
||||
|
||||
export type SecretVersionSelector = number | "latest";
|
||||
|
||||
export interface EnvPlainBinding {
|
||||
type: "plain";
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface EnvSecretRefBinding {
|
||||
type: "secret_ref";
|
||||
secretId: string;
|
||||
version?: SecretVersionSelector;
|
||||
}
|
||||
|
||||
// Backward-compatible: legacy plaintext string values are still accepted.
|
||||
export type EnvBinding = string | EnvPlainBinding | EnvSecretRefBinding;
|
||||
|
||||
export type AgentEnvConfig = Record<string, EnvBinding>;
|
||||
|
||||
export interface CompanySecret {
|
||||
id: string;
|
||||
companyId: string;
|
||||
name: string;
|
||||
provider: SecretProvider;
|
||||
externalRef: string | null;
|
||||
latestVersion: number;
|
||||
description: string | null;
|
||||
createdByAgentId: string | null;
|
||||
createdByUserId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface SecretProviderDescriptor {
|
||||
id: SecretProvider;
|
||||
label: string;
|
||||
requiresExternalRef: boolean;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface SidebarBadges {
|
||||
inbox: number;
|
||||
approvals: number;
|
||||
failedRuns: number;
|
||||
joinRequests: number;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
export type IssueWorkProductType =
|
||||
| "preview_url"
|
||||
| "runtime_service"
|
||||
| "pull_request"
|
||||
| "branch"
|
||||
| "commit"
|
||||
| "artifact"
|
||||
| "document";
|
||||
|
||||
export type IssueWorkProductProvider =
|
||||
| "paperclip"
|
||||
| "github"
|
||||
| "vercel"
|
||||
| "s3"
|
||||
| "custom";
|
||||
|
||||
export type IssueWorkProductStatus =
|
||||
| "active"
|
||||
| "ready_for_review"
|
||||
| "approved"
|
||||
| "changes_requested"
|
||||
| "merged"
|
||||
| "closed"
|
||||
| "failed"
|
||||
| "archived"
|
||||
| "draft";
|
||||
|
||||
export type IssueWorkProductReviewState =
|
||||
| "none"
|
||||
| "needs_board_review"
|
||||
| "approved"
|
||||
| "changes_requested";
|
||||
|
||||
export interface IssueWorkProduct {
|
||||
id: string;
|
||||
companyId: string;
|
||||
projectId: string | null;
|
||||
issueId: string;
|
||||
executionWorkspaceId: string | null;
|
||||
runtimeServiceId: string | null;
|
||||
type: IssueWorkProductType;
|
||||
provider: IssueWorkProductProvider | string;
|
||||
externalId: string | null;
|
||||
title: string;
|
||||
url: string | null;
|
||||
status: IssueWorkProductStatus | string;
|
||||
reviewState: IssueWorkProductReviewState;
|
||||
isPrimary: boolean;
|
||||
healthStatus: "unknown" | "healthy" | "unhealthy";
|
||||
summary: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
createdByRunId: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
export type WorkspaceOperationPhase =
|
||||
| "worktree_prepare"
|
||||
| "workspace_provision"
|
||||
| "workspace_teardown"
|
||||
| "worktree_cleanup";
|
||||
|
||||
export type WorkspaceOperationStatus = "running" | "succeeded" | "failed" | "skipped";
|
||||
|
||||
export interface WorkspaceOperation {
|
||||
id: string;
|
||||
companyId: string;
|
||||
executionWorkspaceId: string | null;
|
||||
heartbeatRunId: string | null;
|
||||
phase: WorkspaceOperationPhase;
|
||||
command: string | null;
|
||||
cwd: string | null;
|
||||
status: WorkspaceOperationStatus;
|
||||
exitCode: number | null;
|
||||
logStore: string | null;
|
||||
logRef: string | null;
|
||||
logBytes: number | null;
|
||||
logSha256: string | null;
|
||||
logCompressed: boolean;
|
||||
stdoutExcerpt: string | null;
|
||||
stderrExcerpt: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
startedAt: Date;
|
||||
finishedAt: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
export type ExecutionWorkspaceStrategyType =
|
||||
| "project_primary"
|
||||
| "git_worktree"
|
||||
| "adapter_managed"
|
||||
| "cloud_sandbox";
|
||||
|
||||
export type ProjectExecutionWorkspaceDefaultMode =
|
||||
| "shared_workspace"
|
||||
| "isolated_workspace"
|
||||
| "operator_branch"
|
||||
| "adapter_default";
|
||||
|
||||
export type ExecutionWorkspaceMode =
|
||||
| "inherit"
|
||||
| "shared_workspace"
|
||||
| "isolated_workspace"
|
||||
| "operator_branch"
|
||||
| "reuse_existing"
|
||||
| "agent_default";
|
||||
|
||||
export type ExecutionWorkspaceProviderType =
|
||||
| "local_fs"
|
||||
| "git_worktree"
|
||||
| "adapter_managed"
|
||||
| "cloud_sandbox";
|
||||
|
||||
export type ExecutionWorkspaceStatus =
|
||||
| "active"
|
||||
| "idle"
|
||||
| "in_review"
|
||||
| "archived"
|
||||
| "cleanup_failed";
|
||||
|
||||
export interface ExecutionWorkspaceStrategy {
|
||||
type: ExecutionWorkspaceStrategyType;
|
||||
baseRef?: string | null;
|
||||
branchTemplate?: string | null;
|
||||
worktreeParentDir?: string | null;
|
||||
provisionCommand?: string | null;
|
||||
teardownCommand?: string | null;
|
||||
}
|
||||
|
||||
export interface ProjectExecutionWorkspacePolicy {
|
||||
enabled: boolean;
|
||||
defaultMode?: ProjectExecutionWorkspaceDefaultMode;
|
||||
allowIssueOverride?: boolean;
|
||||
defaultProjectWorkspaceId?: string | null;
|
||||
workspaceStrategy?: ExecutionWorkspaceStrategy | null;
|
||||
workspaceRuntime?: Record<string, unknown> | null;
|
||||
branchPolicy?: Record<string, unknown> | null;
|
||||
pullRequestPolicy?: Record<string, unknown> | null;
|
||||
runtimePolicy?: Record<string, unknown> | null;
|
||||
cleanupPolicy?: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface IssueExecutionWorkspaceSettings {
|
||||
mode?: ExecutionWorkspaceMode;
|
||||
workspaceStrategy?: ExecutionWorkspaceStrategy | null;
|
||||
workspaceRuntime?: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface ExecutionWorkspace {
|
||||
id: string;
|
||||
companyId: string;
|
||||
projectId: string;
|
||||
projectWorkspaceId: string | null;
|
||||
sourceIssueId: string | null;
|
||||
mode: Exclude<ExecutionWorkspaceMode, "inherit" | "reuse_existing" | "agent_default"> | "adapter_managed" | "cloud_sandbox";
|
||||
strategyType: ExecutionWorkspaceStrategyType;
|
||||
name: string;
|
||||
status: ExecutionWorkspaceStatus;
|
||||
cwd: string | null;
|
||||
repoUrl: string | null;
|
||||
baseRef: string | null;
|
||||
branchName: string | null;
|
||||
providerType: ExecutionWorkspaceProviderType;
|
||||
providerRef: string | null;
|
||||
derivedFromExecutionWorkspaceId: string | null;
|
||||
lastUsedAt: Date;
|
||||
openedAt: Date;
|
||||
closedAt: Date | null;
|
||||
cleanupEligibleAt: Date | null;
|
||||
cleanupReason: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface WorkspaceRuntimeService {
|
||||
id: string;
|
||||
companyId: string;
|
||||
projectId: string | null;
|
||||
projectWorkspaceId: string | null;
|
||||
executionWorkspaceId: string | null;
|
||||
issueId: string | null;
|
||||
scopeType: "project_workspace" | "execution_workspace" | "run" | "agent";
|
||||
scopeId: string | null;
|
||||
serviceName: string;
|
||||
status: "starting" | "running" | "stopped" | "failed";
|
||||
lifecycle: "shared" | "ephemeral";
|
||||
reuseKey: string | null;
|
||||
command: string | null;
|
||||
cwd: string | null;
|
||||
port: number | null;
|
||||
url: string | null;
|
||||
provider: "local_process" | "adapter_managed";
|
||||
providerRef: string | null;
|
||||
ownerAgentId: string | null;
|
||||
startedByRunId: string | null;
|
||||
lastUsedAt: Date;
|
||||
startedAt: Date;
|
||||
stoppedAt: Date | null;
|
||||
stopPolicy: Record<string, unknown> | null;
|
||||
healthStatus: "unknown" | "healthy" | "unhealthy";
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AGENT_ADAPTER_TYPES,
|
||||
INVITE_JOIN_TYPES,
|
||||
JOIN_REQUEST_STATUSES,
|
||||
JOIN_REQUEST_TYPES,
|
||||
PERMISSION_KEYS,
|
||||
} from "../constants.js";
|
||||
|
||||
export const createCompanyInviteSchema = z.object({
|
||||
allowedJoinTypes: z.enum(INVITE_JOIN_TYPES).default("both"),
|
||||
defaultsPayload: z.record(z.string(), z.unknown()).optional().nullable(),
|
||||
agentMessage: z.string().max(4000).optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateCompanyInvite = z.infer<typeof createCompanyInviteSchema>;
|
||||
|
||||
export const createOpenClawInvitePromptSchema = z.object({
|
||||
agentMessage: z.string().max(4000).optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateOpenClawInvitePrompt = z.infer<
|
||||
typeof createOpenClawInvitePromptSchema
|
||||
>;
|
||||
|
||||
export const acceptInviteSchema = z.object({
|
||||
requestType: z.enum(JOIN_REQUEST_TYPES),
|
||||
agentName: z.string().min(1).max(120).optional(),
|
||||
adapterType: z.enum(AGENT_ADAPTER_TYPES).optional(),
|
||||
capabilities: z.string().max(4000).optional().nullable(),
|
||||
agentDefaultsPayload: z.record(z.string(), z.unknown()).optional().nullable(),
|
||||
// OpenClaw join compatibility fields accepted at top level.
|
||||
responsesWebhookUrl: z.string().max(4000).optional().nullable(),
|
||||
responsesWebhookMethod: z.string().max(32).optional().nullable(),
|
||||
responsesWebhookHeaders: z.record(z.string(), z.unknown()).optional().nullable(),
|
||||
paperclipApiUrl: z.string().max(4000).optional().nullable(),
|
||||
webhookAuthHeader: z.string().max(4000).optional().nullable(),
|
||||
});
|
||||
|
||||
export type AcceptInvite = z.infer<typeof acceptInviteSchema>;
|
||||
|
||||
export const listJoinRequestsQuerySchema = z.object({
|
||||
status: z.enum(JOIN_REQUEST_STATUSES).optional(),
|
||||
requestType: z.enum(JOIN_REQUEST_TYPES).optional(),
|
||||
});
|
||||
|
||||
export type ListJoinRequestsQuery = z.infer<typeof listJoinRequestsQuerySchema>;
|
||||
|
||||
export const claimJoinRequestApiKeySchema = z.object({
|
||||
claimSecret: z.string().min(16).max(256),
|
||||
});
|
||||
|
||||
export type ClaimJoinRequestApiKey = z.infer<typeof claimJoinRequestApiKeySchema>;
|
||||
|
||||
export const boardCliAuthAccessLevelSchema = z.enum([
|
||||
"board",
|
||||
"instance_admin_required",
|
||||
]);
|
||||
|
||||
export type BoardCliAuthAccessLevel = z.infer<typeof boardCliAuthAccessLevelSchema>;
|
||||
|
||||
export const createCliAuthChallengeSchema = z.object({
|
||||
command: z.string().min(1).max(240),
|
||||
clientName: z.string().max(120).optional().nullable(),
|
||||
requestedAccess: boardCliAuthAccessLevelSchema.default("board"),
|
||||
requestedCompanyId: z.string().uuid().optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateCliAuthChallenge = z.infer<typeof createCliAuthChallengeSchema>;
|
||||
|
||||
export const resolveCliAuthChallengeSchema = z.object({
|
||||
token: z.string().min(16).max(256),
|
||||
});
|
||||
|
||||
export type ResolveCliAuthChallenge = z.infer<typeof resolveCliAuthChallengeSchema>;
|
||||
|
||||
export const updateMemberPermissionsSchema = z.object({
|
||||
grants: z.array(
|
||||
z.object({
|
||||
permissionKey: z.enum(PERMISSION_KEYS),
|
||||
scope: z.record(z.string(), z.unknown()).optional().nullable(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export type UpdateMemberPermissions = z.infer<typeof updateMemberPermissionsSchema>;
|
||||
|
||||
export const updateUserCompanyAccessSchema = z.object({
|
||||
companyIds: z.array(z.string().uuid()).default([]),
|
||||
});
|
||||
|
||||
export type UpdateUserCompanyAccess = z.infer<typeof updateUserCompanyAccessSchema>;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const agentSkillStateSchema = z.enum([
|
||||
"available",
|
||||
"configured",
|
||||
"installed",
|
||||
"missing",
|
||||
"stale",
|
||||
"external",
|
||||
]);
|
||||
|
||||
export const agentSkillOriginSchema = z.enum([
|
||||
"company_managed",
|
||||
"paperclip_required",
|
||||
"user_installed",
|
||||
"external_unknown",
|
||||
]);
|
||||
|
||||
export const agentSkillSyncModeSchema = z.enum([
|
||||
"unsupported",
|
||||
"persistent",
|
||||
"ephemeral",
|
||||
]);
|
||||
|
||||
export const agentSkillEntrySchema = z.object({
|
||||
key: z.string().min(1),
|
||||
runtimeName: z.string().min(1).nullable(),
|
||||
desired: z.boolean(),
|
||||
managed: z.boolean(),
|
||||
required: z.boolean().optional(),
|
||||
requiredReason: z.string().nullable().optional(),
|
||||
state: agentSkillStateSchema,
|
||||
origin: agentSkillOriginSchema.optional(),
|
||||
originLabel: z.string().nullable().optional(),
|
||||
locationLabel: z.string().nullable().optional(),
|
||||
readOnly: z.boolean().optional(),
|
||||
sourcePath: z.string().nullable().optional(),
|
||||
targetPath: z.string().nullable().optional(),
|
||||
detail: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const agentSkillSnapshotSchema = z.object({
|
||||
adapterType: z.string().min(1),
|
||||
supported: z.boolean(),
|
||||
mode: agentSkillSyncModeSchema,
|
||||
desiredSkills: z.array(z.string().min(1)),
|
||||
entries: z.array(agentSkillEntrySchema),
|
||||
warnings: z.array(z.string()),
|
||||
});
|
||||
|
||||
export const agentSkillSyncSchema = z.object({
|
||||
desiredSkills: z.array(z.string().min(1)),
|
||||
});
|
||||
|
||||
export type AgentSkillSync = z.infer<typeof agentSkillSyncSchema>;
|
||||
@@ -0,0 +1,127 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AGENT_ADAPTER_TYPES,
|
||||
AGENT_ICON_NAMES,
|
||||
AGENT_ROLES,
|
||||
AGENT_STATUSES,
|
||||
} from "../constants.js";
|
||||
import { envConfigSchema } from "./secret.js";
|
||||
|
||||
export const agentPermissionsSchema = z.object({
|
||||
canCreateAgents: z.boolean().optional().default(false),
|
||||
});
|
||||
|
||||
export const agentInstructionsBundleModeSchema = z.enum(["managed", "external"]);
|
||||
|
||||
export const updateAgentInstructionsBundleSchema = z.object({
|
||||
mode: agentInstructionsBundleModeSchema.optional(),
|
||||
rootPath: z.string().trim().min(1).nullable().optional(),
|
||||
entryFile: z.string().trim().min(1).optional(),
|
||||
clearLegacyPromptTemplate: z.boolean().optional().default(false),
|
||||
});
|
||||
|
||||
export type UpdateAgentInstructionsBundle = z.infer<typeof updateAgentInstructionsBundleSchema>;
|
||||
|
||||
export const upsertAgentInstructionsFileSchema = z.object({
|
||||
path: z.string().trim().min(1),
|
||||
content: z.string(),
|
||||
clearLegacyPromptTemplate: z.boolean().optional().default(false),
|
||||
});
|
||||
|
||||
export type UpsertAgentInstructionsFile = z.infer<typeof upsertAgentInstructionsFileSchema>;
|
||||
|
||||
const adapterConfigSchema = z.record(z.unknown()).superRefine((value, ctx) => {
|
||||
const envValue = value.env;
|
||||
if (envValue === undefined) return;
|
||||
const parsed = envConfigSchema.safeParse(envValue);
|
||||
if (!parsed.success) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "adapterConfig.env must be a map of valid env bindings",
|
||||
path: ["env"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const createAgentSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
role: z.enum(AGENT_ROLES).optional().default("general"),
|
||||
title: z.string().optional().nullable(),
|
||||
icon: z.enum(AGENT_ICON_NAMES).optional().nullable(),
|
||||
reportsTo: z.string().uuid().optional().nullable(),
|
||||
capabilities: z.string().optional().nullable(),
|
||||
desiredSkills: z.array(z.string().min(1)).optional(),
|
||||
adapterType: z.enum(AGENT_ADAPTER_TYPES).optional().default("process"),
|
||||
adapterConfig: adapterConfigSchema.optional().default({}),
|
||||
runtimeConfig: z.record(z.unknown()).optional().default({}),
|
||||
budgetMonthlyCents: z.number().int().nonnegative().optional().default(0),
|
||||
permissions: agentPermissionsSchema.optional(),
|
||||
metadata: z.record(z.unknown()).optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateAgent = z.infer<typeof createAgentSchema>;
|
||||
|
||||
export const createAgentHireSchema = createAgentSchema.extend({
|
||||
sourceIssueId: z.string().uuid().optional().nullable(),
|
||||
sourceIssueIds: z.array(z.string().uuid()).optional(),
|
||||
});
|
||||
|
||||
export type CreateAgentHire = z.infer<typeof createAgentHireSchema>;
|
||||
|
||||
export const updateAgentSchema = createAgentSchema
|
||||
.omit({ permissions: true })
|
||||
.partial()
|
||||
.extend({
|
||||
permissions: z.never().optional(),
|
||||
replaceAdapterConfig: z.boolean().optional(),
|
||||
status: z.enum(AGENT_STATUSES).optional(),
|
||||
spentMonthlyCents: z.number().int().nonnegative().optional(),
|
||||
});
|
||||
|
||||
export type UpdateAgent = z.infer<typeof updateAgentSchema>;
|
||||
|
||||
export const updateAgentInstructionsPathSchema = z.object({
|
||||
path: z.string().trim().min(1).nullable(),
|
||||
adapterConfigKey: z.string().trim().min(1).optional(),
|
||||
});
|
||||
|
||||
export type UpdateAgentInstructionsPath = z.infer<typeof updateAgentInstructionsPathSchema>;
|
||||
|
||||
export const createAgentKeySchema = z.object({
|
||||
name: z.string().min(1).default("default"),
|
||||
});
|
||||
|
||||
export type CreateAgentKey = z.infer<typeof createAgentKeySchema>;
|
||||
|
||||
export const wakeAgentSchema = z.object({
|
||||
source: z.enum(["timer", "assignment", "on_demand", "automation"]).optional().default("on_demand"),
|
||||
triggerDetail: z.enum(["manual", "ping", "callback", "system"]).optional(),
|
||||
reason: z.string().optional().nullable(),
|
||||
payload: z.record(z.unknown()).optional().nullable(),
|
||||
idempotencyKey: z.string().optional().nullable(),
|
||||
forceFreshSession: z.preprocess(
|
||||
(value) => (value === null ? undefined : value),
|
||||
z.boolean().optional().default(false),
|
||||
),
|
||||
});
|
||||
|
||||
export type WakeAgent = z.infer<typeof wakeAgentSchema>;
|
||||
|
||||
export const resetAgentSessionSchema = z.object({
|
||||
taskKey: z.string().min(1).optional().nullable(),
|
||||
});
|
||||
|
||||
export type ResetAgentSession = z.infer<typeof resetAgentSessionSchema>;
|
||||
|
||||
export const testAdapterEnvironmentSchema = z.object({
|
||||
adapterConfig: adapterConfigSchema.optional().default({}),
|
||||
});
|
||||
|
||||
export type TestAdapterEnvironment = z.infer<typeof testAdapterEnvironmentSchema>;
|
||||
|
||||
export const updateAgentPermissionsSchema = z.object({
|
||||
canCreateAgents: z.boolean(),
|
||||
canAssignTasks: z.boolean(),
|
||||
});
|
||||
|
||||
export type UpdateAgentPermissions = z.infer<typeof updateAgentPermissionsSchema>;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { z } from "zod";
|
||||
import { APPROVAL_TYPES } from "../constants.js";
|
||||
|
||||
export const createApprovalSchema = z.object({
|
||||
type: z.enum(APPROVAL_TYPES),
|
||||
requestedByAgentId: z.string().uuid().optional().nullable(),
|
||||
payload: z.record(z.unknown()),
|
||||
issueIds: z.array(z.string().uuid()).optional(),
|
||||
});
|
||||
|
||||
export type CreateApproval = z.infer<typeof createApprovalSchema>;
|
||||
|
||||
export const resolveApprovalSchema = z.object({
|
||||
decisionNote: z.string().optional().nullable(),
|
||||
decidedByUserId: z.string().optional().default("board"),
|
||||
});
|
||||
|
||||
export type ResolveApproval = z.infer<typeof resolveApprovalSchema>;
|
||||
|
||||
export const requestApprovalRevisionSchema = z.object({
|
||||
decisionNote: z.string().optional().nullable(),
|
||||
decidedByUserId: z.string().optional().default("board"),
|
||||
});
|
||||
|
||||
export type RequestApprovalRevision = z.infer<typeof requestApprovalRevisionSchema>;
|
||||
|
||||
export const resubmitApprovalSchema = z.object({
|
||||
payload: z.record(z.unknown()).optional(),
|
||||
});
|
||||
|
||||
export type ResubmitApproval = z.infer<typeof resubmitApprovalSchema>;
|
||||
|
||||
export const addApprovalCommentSchema = z.object({
|
||||
body: z.string().min(1),
|
||||
});
|
||||
|
||||
export type AddApprovalComment = z.infer<typeof addApprovalCommentSchema>;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const createAssetImageMetadataSchema = z.object({
|
||||
namespace: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(120)
|
||||
.regex(/^[a-zA-Z0-9/_-]+$/)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type CreateAssetImageMetadata = z.infer<typeof createAssetImageMetadataSchema>;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
BUDGET_INCIDENT_RESOLUTION_ACTIONS,
|
||||
BUDGET_METRICS,
|
||||
BUDGET_SCOPE_TYPES,
|
||||
BUDGET_WINDOW_KINDS,
|
||||
} from "../constants.js";
|
||||
|
||||
export const upsertBudgetPolicySchema = z.object({
|
||||
scopeType: z.enum(BUDGET_SCOPE_TYPES),
|
||||
scopeId: z.string().uuid(),
|
||||
metric: z.enum(BUDGET_METRICS).optional().default("billed_cents"),
|
||||
windowKind: z.enum(BUDGET_WINDOW_KINDS).optional().default("calendar_month_utc"),
|
||||
amount: z.number().int().nonnegative(),
|
||||
warnPercent: z.number().int().min(1).max(99).optional().default(80),
|
||||
hardStopEnabled: z.boolean().optional().default(true),
|
||||
notifyEnabled: z.boolean().optional().default(true),
|
||||
isActive: z.boolean().optional().default(true),
|
||||
});
|
||||
|
||||
export type UpsertBudgetPolicy = z.infer<typeof upsertBudgetPolicySchema>;
|
||||
|
||||
export const resolveBudgetIncidentSchema = z.object({
|
||||
action: z.enum(BUDGET_INCIDENT_RESOLUTION_ACTIONS),
|
||||
amount: z.number().int().nonnegative().optional(),
|
||||
decisionNote: z.string().optional().nullable(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (value.action === "raise_budget_and_resume" && typeof value.amount !== "number") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "amount is required when raising a budget",
|
||||
path: ["amount"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type ResolveBudgetIncident = z.infer<typeof resolveBudgetIncidentSchema>;
|
||||
@@ -0,0 +1,236 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const portabilityIncludeSchema = z
|
||||
.object({
|
||||
company: z.boolean().optional(),
|
||||
agents: z.boolean().optional(),
|
||||
projects: z.boolean().optional(),
|
||||
issues: z.boolean().optional(),
|
||||
skills: z.boolean().optional(),
|
||||
})
|
||||
.partial();
|
||||
|
||||
export const portabilityEnvInputSchema = z.object({
|
||||
key: z.string().min(1),
|
||||
description: z.string().nullable(),
|
||||
agentSlug: z.string().min(1).nullable(),
|
||||
kind: z.enum(["secret", "plain"]),
|
||||
requirement: z.enum(["required", "optional"]),
|
||||
defaultValue: z.string().nullable(),
|
||||
portability: z.enum(["portable", "system_dependent"]),
|
||||
});
|
||||
|
||||
export const portabilityFileEntrySchema = z.union([
|
||||
z.string(),
|
||||
z.object({
|
||||
encoding: z.literal("base64"),
|
||||
data: z.string(),
|
||||
contentType: z.string().min(1).optional().nullable(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const portabilityCompanyManifestEntrySchema = z.object({
|
||||
path: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
description: z.string().nullable(),
|
||||
brandColor: z.string().nullable(),
|
||||
logoPath: z.string().nullable(),
|
||||
requireBoardApprovalForNewAgents: z.boolean(),
|
||||
});
|
||||
|
||||
export const portabilitySidebarOrderSchema = z.object({
|
||||
agents: z.array(z.string().min(1)).default([]),
|
||||
projects: z.array(z.string().min(1)).default([]),
|
||||
});
|
||||
|
||||
export const portabilityAgentManifestEntrySchema = z.object({
|
||||
slug: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
skills: z.array(z.string().min(1)).default([]),
|
||||
role: z.string().min(1),
|
||||
title: z.string().nullable(),
|
||||
icon: z.string().nullable(),
|
||||
capabilities: z.string().nullable(),
|
||||
reportsToSlug: z.string().min(1).nullable(),
|
||||
adapterType: z.string().min(1),
|
||||
adapterConfig: z.record(z.unknown()),
|
||||
runtimeConfig: z.record(z.unknown()),
|
||||
permissions: z.record(z.unknown()),
|
||||
budgetMonthlyCents: z.number().int().nonnegative(),
|
||||
metadata: z.record(z.unknown()).nullable(),
|
||||
});
|
||||
|
||||
export const portabilitySkillManifestEntrySchema = z.object({
|
||||
key: z.string().min(1),
|
||||
slug: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
description: z.string().nullable(),
|
||||
sourceType: z.string().min(1),
|
||||
sourceLocator: z.string().nullable(),
|
||||
sourceRef: z.string().nullable(),
|
||||
trustLevel: z.string().nullable(),
|
||||
compatibility: z.string().nullable(),
|
||||
metadata: z.record(z.unknown()).nullable(),
|
||||
fileInventory: z.array(z.object({
|
||||
path: z.string().min(1),
|
||||
kind: z.string().min(1),
|
||||
})).default([]),
|
||||
});
|
||||
|
||||
export const portabilityProjectManifestEntrySchema = z.object({
|
||||
slug: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
description: z.string().nullable(),
|
||||
ownerAgentSlug: z.string().min(1).nullable(),
|
||||
leadAgentSlug: z.string().min(1).nullable(),
|
||||
targetDate: z.string().nullable(),
|
||||
color: z.string().nullable(),
|
||||
status: z.string().nullable(),
|
||||
executionWorkspacePolicy: z.record(z.unknown()).nullable(),
|
||||
workspaces: z.array(z.object({
|
||||
key: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
sourceType: z.string().nullable(),
|
||||
repoUrl: z.string().nullable(),
|
||||
repoRef: z.string().nullable(),
|
||||
defaultRef: z.string().nullable(),
|
||||
visibility: z.string().nullable(),
|
||||
setupCommand: z.string().nullable(),
|
||||
cleanupCommand: z.string().nullable(),
|
||||
metadata: z.record(z.unknown()).nullable(),
|
||||
isPrimary: z.boolean(),
|
||||
})).default([]),
|
||||
metadata: z.record(z.unknown()).nullable(),
|
||||
});
|
||||
|
||||
export const portabilityIssueRoutineTriggerManifestEntrySchema = z.object({
|
||||
kind: z.string().min(1),
|
||||
label: z.string().nullable(),
|
||||
enabled: z.boolean(),
|
||||
cronExpression: z.string().nullable(),
|
||||
timezone: z.string().nullable(),
|
||||
signingMode: z.string().nullable(),
|
||||
replayWindowSec: z.number().int().nullable(),
|
||||
});
|
||||
|
||||
export const portabilityIssueRoutineManifestEntrySchema = z.object({
|
||||
concurrencyPolicy: z.string().nullable(),
|
||||
catchUpPolicy: z.string().nullable(),
|
||||
triggers: z.array(portabilityIssueRoutineTriggerManifestEntrySchema).default([]),
|
||||
});
|
||||
|
||||
export const portabilityIssueManifestEntrySchema = z.object({
|
||||
slug: z.string().min(1),
|
||||
identifier: z.string().min(1).nullable(),
|
||||
title: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
projectSlug: z.string().min(1).nullable(),
|
||||
projectWorkspaceKey: z.string().min(1).nullable(),
|
||||
assigneeAgentSlug: z.string().min(1).nullable(),
|
||||
description: z.string().nullable(),
|
||||
recurring: z.boolean().default(false),
|
||||
routine: portabilityIssueRoutineManifestEntrySchema.nullable(),
|
||||
legacyRecurrence: z.record(z.unknown()).nullable(),
|
||||
status: z.string().nullable(),
|
||||
priority: z.string().nullable(),
|
||||
labelIds: z.array(z.string().min(1)).default([]),
|
||||
billingCode: z.string().nullable(),
|
||||
executionWorkspaceSettings: z.record(z.unknown()).nullable(),
|
||||
assigneeAdapterOverrides: z.record(z.unknown()).nullable(),
|
||||
metadata: z.record(z.unknown()).nullable(),
|
||||
});
|
||||
|
||||
export const portabilityManifestSchema = z.object({
|
||||
schemaVersion: z.number().int().positive(),
|
||||
generatedAt: z.string().datetime(),
|
||||
source: z
|
||||
.object({
|
||||
companyId: z.string().uuid(),
|
||||
companyName: z.string().min(1),
|
||||
})
|
||||
.nullable(),
|
||||
includes: z.object({
|
||||
company: z.boolean(),
|
||||
agents: z.boolean(),
|
||||
projects: z.boolean(),
|
||||
issues: z.boolean(),
|
||||
skills: z.boolean(),
|
||||
}),
|
||||
company: portabilityCompanyManifestEntrySchema.nullable(),
|
||||
sidebar: portabilitySidebarOrderSchema.nullable(),
|
||||
agents: z.array(portabilityAgentManifestEntrySchema),
|
||||
skills: z.array(portabilitySkillManifestEntrySchema).default([]),
|
||||
projects: z.array(portabilityProjectManifestEntrySchema).default([]),
|
||||
issues: z.array(portabilityIssueManifestEntrySchema).default([]),
|
||||
envInputs: z.array(portabilityEnvInputSchema).default([]),
|
||||
});
|
||||
|
||||
export const portabilitySourceSchema = z.discriminatedUnion("type", [
|
||||
z.object({
|
||||
type: z.literal("inline"),
|
||||
rootPath: z.string().min(1).optional().nullable(),
|
||||
files: z.record(portabilityFileEntrySchema),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal("github"),
|
||||
url: z.string().url(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const portabilityTargetSchema = z.discriminatedUnion("mode", [
|
||||
z.object({
|
||||
mode: z.literal("new_company"),
|
||||
newCompanyName: z.string().min(1).optional().nullable(),
|
||||
}),
|
||||
z.object({
|
||||
mode: z.literal("existing_company"),
|
||||
companyId: z.string().uuid(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const portabilityAgentSelectionSchema = z.union([
|
||||
z.literal("all"),
|
||||
z.array(z.string().min(1)),
|
||||
]);
|
||||
|
||||
export const portabilityCollisionStrategySchema = z.enum(["rename", "skip", "replace"]);
|
||||
|
||||
export const companyPortabilityExportSchema = z.object({
|
||||
include: portabilityIncludeSchema.optional(),
|
||||
agents: z.array(z.string().min(1)).optional(),
|
||||
skills: z.array(z.string().min(1)).optional(),
|
||||
projects: z.array(z.string().min(1)).optional(),
|
||||
issues: z.array(z.string().min(1)).optional(),
|
||||
projectIssues: z.array(z.string().min(1)).optional(),
|
||||
selectedFiles: z.array(z.string().min(1)).optional(),
|
||||
expandReferencedSkills: z.boolean().optional(),
|
||||
sidebarOrder: portabilitySidebarOrderSchema.partial().optional(),
|
||||
});
|
||||
|
||||
export type CompanyPortabilityExport = z.infer<typeof companyPortabilityExportSchema>;
|
||||
|
||||
export const companyPortabilityPreviewSchema = z.object({
|
||||
source: portabilitySourceSchema,
|
||||
include: portabilityIncludeSchema.optional(),
|
||||
target: portabilityTargetSchema,
|
||||
agents: portabilityAgentSelectionSchema.optional(),
|
||||
collisionStrategy: portabilityCollisionStrategySchema.optional(),
|
||||
nameOverrides: z.record(z.string().min(1), z.string().min(1)).optional(),
|
||||
selectedFiles: z.array(z.string().min(1)).optional(),
|
||||
});
|
||||
|
||||
export type CompanyPortabilityPreview = z.infer<typeof companyPortabilityPreviewSchema>;
|
||||
|
||||
export const portabilityAdapterOverrideSchema = z.object({
|
||||
adapterType: z.string().min(1),
|
||||
adapterConfig: z.record(z.unknown()).optional(),
|
||||
});
|
||||
|
||||
export const companyPortabilityImportSchema = companyPortabilityPreviewSchema.extend({
|
||||
adapterOverrides: z.record(z.string().min(1), portabilityAdapterOverrideSchema).optional(),
|
||||
});
|
||||
|
||||
export type CompanyPortabilityImport = z.infer<typeof companyPortabilityImportSchema>;
|
||||
@@ -0,0 +1,135 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const companySkillSourceTypeSchema = z.enum(["local_path", "github", "url", "catalog", "skills_sh"]);
|
||||
export const companySkillTrustLevelSchema = z.enum(["markdown_only", "assets", "scripts_executables"]);
|
||||
export const companySkillCompatibilitySchema = z.enum(["compatible", "unknown", "invalid"]);
|
||||
export const companySkillSourceBadgeSchema = z.enum(["paperclip", "github", "local", "url", "catalog", "skills_sh"]);
|
||||
|
||||
export const companySkillFileInventoryEntrySchema = z.object({
|
||||
path: z.string().min(1),
|
||||
kind: z.enum(["skill", "markdown", "reference", "script", "asset", "other"]),
|
||||
});
|
||||
|
||||
export const companySkillSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
key: z.string().min(1),
|
||||
slug: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
description: z.string().nullable(),
|
||||
markdown: z.string(),
|
||||
sourceType: companySkillSourceTypeSchema,
|
||||
sourceLocator: z.string().nullable(),
|
||||
sourceRef: z.string().nullable(),
|
||||
trustLevel: companySkillTrustLevelSchema,
|
||||
compatibility: companySkillCompatibilitySchema,
|
||||
fileInventory: z.array(companySkillFileInventoryEntrySchema).default([]),
|
||||
metadata: z.record(z.unknown()).nullable(),
|
||||
createdAt: z.coerce.date(),
|
||||
updatedAt: z.coerce.date(),
|
||||
});
|
||||
|
||||
export const companySkillListItemSchema = companySkillSchema.extend({
|
||||
attachedAgentCount: z.number().int().nonnegative(),
|
||||
editable: z.boolean(),
|
||||
editableReason: z.string().nullable(),
|
||||
sourceLabel: z.string().nullable(),
|
||||
sourceBadge: companySkillSourceBadgeSchema,
|
||||
});
|
||||
|
||||
export const companySkillUsageAgentSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string().min(1),
|
||||
urlKey: z.string().min(1),
|
||||
adapterType: z.string().min(1),
|
||||
desired: z.boolean(),
|
||||
actualState: z.string().nullable(),
|
||||
});
|
||||
|
||||
export const companySkillDetailSchema = companySkillSchema.extend({
|
||||
attachedAgentCount: z.number().int().nonnegative(),
|
||||
usedByAgents: z.array(companySkillUsageAgentSchema).default([]),
|
||||
editable: z.boolean(),
|
||||
editableReason: z.string().nullable(),
|
||||
sourceLabel: z.string().nullable(),
|
||||
sourceBadge: companySkillSourceBadgeSchema,
|
||||
});
|
||||
|
||||
export const companySkillUpdateStatusSchema = z.object({
|
||||
supported: z.boolean(),
|
||||
reason: z.string().nullable(),
|
||||
trackingRef: z.string().nullable(),
|
||||
currentRef: z.string().nullable(),
|
||||
latestRef: z.string().nullable(),
|
||||
hasUpdate: z.boolean(),
|
||||
});
|
||||
|
||||
export const companySkillImportSchema = z.object({
|
||||
source: z.string().min(1),
|
||||
});
|
||||
|
||||
export const companySkillProjectScanRequestSchema = z.object({
|
||||
projectIds: z.array(z.string().uuid()).optional(),
|
||||
workspaceIds: z.array(z.string().uuid()).optional(),
|
||||
});
|
||||
|
||||
export const companySkillProjectScanSkippedSchema = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
projectName: z.string().min(1),
|
||||
workspaceId: z.string().uuid().nullable(),
|
||||
workspaceName: z.string().nullable(),
|
||||
path: z.string().nullable(),
|
||||
reason: z.string().min(1),
|
||||
});
|
||||
|
||||
export const companySkillProjectScanConflictSchema = z.object({
|
||||
slug: z.string().min(1),
|
||||
key: z.string().min(1),
|
||||
projectId: z.string().uuid(),
|
||||
projectName: z.string().min(1),
|
||||
workspaceId: z.string().uuid(),
|
||||
workspaceName: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
existingSkillId: z.string().uuid(),
|
||||
existingSkillKey: z.string().min(1),
|
||||
existingSourceLocator: z.string().nullable(),
|
||||
reason: z.string().min(1),
|
||||
});
|
||||
|
||||
export const companySkillProjectScanResultSchema = z.object({
|
||||
scannedProjects: z.number().int().nonnegative(),
|
||||
scannedWorkspaces: z.number().int().nonnegative(),
|
||||
discovered: z.number().int().nonnegative(),
|
||||
imported: z.array(companySkillSchema),
|
||||
updated: z.array(companySkillSchema),
|
||||
skipped: z.array(companySkillProjectScanSkippedSchema),
|
||||
conflicts: z.array(companySkillProjectScanConflictSchema),
|
||||
warnings: z.array(z.string()),
|
||||
});
|
||||
|
||||
export const companySkillCreateSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
slug: z.string().min(1).nullable().optional(),
|
||||
description: z.string().nullable().optional(),
|
||||
markdown: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const companySkillFileDetailSchema = z.object({
|
||||
skillId: z.string().uuid(),
|
||||
path: z.string().min(1),
|
||||
kind: z.enum(["skill", "markdown", "reference", "script", "asset", "other"]),
|
||||
content: z.string(),
|
||||
language: z.string().nullable(),
|
||||
markdown: z.boolean(),
|
||||
editable: z.boolean(),
|
||||
});
|
||||
|
||||
export const companySkillFileUpdateSchema = z.object({
|
||||
path: z.string().min(1),
|
||||
content: z.string(),
|
||||
});
|
||||
|
||||
export type CompanySkillImport = z.infer<typeof companySkillImportSchema>;
|
||||
export type CompanySkillProjectScan = z.infer<typeof companySkillProjectScanRequestSchema>;
|
||||
export type CompanySkillCreate = z.infer<typeof companySkillCreateSchema>;
|
||||
export type CompanySkillFileUpdate = z.infer<typeof companySkillFileUpdateSchema>;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { z } from "zod";
|
||||
import { COMPANY_STATUSES } from "../constants.js";
|
||||
|
||||
const logoAssetIdSchema = z.string().uuid().nullable().optional();
|
||||
const brandColorSchema = z.string().regex(/^#[0-9a-fA-F]{6}$/).nullable().optional();
|
||||
|
||||
export const createCompanySchema = z.object({
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional().nullable(),
|
||||
budgetMonthlyCents: z.number().int().nonnegative().optional().default(0),
|
||||
});
|
||||
|
||||
export type CreateCompany = z.infer<typeof createCompanySchema>;
|
||||
|
||||
export const updateCompanySchema = createCompanySchema
|
||||
.partial()
|
||||
.extend({
|
||||
status: z.enum(COMPANY_STATUSES).optional(),
|
||||
spentMonthlyCents: z.number().int().nonnegative().optional(),
|
||||
requireBoardApprovalForNewAgents: z.boolean().optional(),
|
||||
brandColor: brandColorSchema,
|
||||
logoAssetId: logoAssetIdSchema,
|
||||
});
|
||||
|
||||
export type UpdateCompany = z.infer<typeof updateCompanySchema>;
|
||||
|
||||
export const updateCompanyBrandingSchema = z
|
||||
.object({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().nullable().optional(),
|
||||
brandColor: brandColorSchema,
|
||||
logoAssetId: logoAssetIdSchema,
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
(value) =>
|
||||
value.name !== undefined
|
||||
|| value.description !== undefined
|
||||
|| value.brandColor !== undefined
|
||||
|| value.logoAssetId !== undefined,
|
||||
"At least one branding field must be provided",
|
||||
);
|
||||
|
||||
export type UpdateCompanyBranding = z.infer<typeof updateCompanyBrandingSchema>;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { z } from "zod";
|
||||
import { BILLING_TYPES } from "../constants.js";
|
||||
|
||||
export const createCostEventSchema = z.object({
|
||||
agentId: z.string().uuid(),
|
||||
issueId: z.string().uuid().optional().nullable(),
|
||||
projectId: z.string().uuid().optional().nullable(),
|
||||
goalId: z.string().uuid().optional().nullable(),
|
||||
heartbeatRunId: z.string().uuid().optional().nullable(),
|
||||
billingCode: z.string().optional().nullable(),
|
||||
provider: z.string().min(1),
|
||||
biller: z.string().min(1).optional(),
|
||||
billingType: z.enum(BILLING_TYPES).optional().default("unknown"),
|
||||
model: z.string().min(1),
|
||||
inputTokens: z.number().int().nonnegative().optional().default(0),
|
||||
cachedInputTokens: z.number().int().nonnegative().optional().default(0),
|
||||
outputTokens: z.number().int().nonnegative().optional().default(0),
|
||||
costCents: z.number().int().nonnegative(),
|
||||
occurredAt: z.string().datetime(),
|
||||
}).transform((value) => ({
|
||||
...value,
|
||||
biller: value.biller ?? value.provider,
|
||||
}));
|
||||
|
||||
export type CreateCostEvent = z.infer<typeof createCostEventSchema>;
|
||||
|
||||
export const updateBudgetSchema = z.object({
|
||||
budgetMonthlyCents: z.number().int().nonnegative(),
|
||||
});
|
||||
|
||||
export type UpdateBudget = z.infer<typeof updateBudgetSchema>;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const executionWorkspaceStatusSchema = z.enum([
|
||||
"active",
|
||||
"idle",
|
||||
"in_review",
|
||||
"archived",
|
||||
"cleanup_failed",
|
||||
]);
|
||||
|
||||
export const updateExecutionWorkspaceSchema = z.object({
|
||||
status: executionWorkspaceStatusSchema.optional(),
|
||||
cleanupEligibleAt: z.string().datetime().optional().nullable(),
|
||||
cleanupReason: z.string().optional().nullable(),
|
||||
metadata: z.record(z.unknown()).optional().nullable(),
|
||||
}).strict();
|
||||
|
||||
export type UpdateExecutionWorkspace = z.infer<typeof updateExecutionWorkspaceSchema>;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { z } from "zod";
|
||||
import { AGENT_ADAPTER_TYPES, FINANCE_DIRECTIONS, FINANCE_EVENT_KINDS, FINANCE_UNITS } from "../constants.js";
|
||||
|
||||
export const createFinanceEventSchema = z.object({
|
||||
agentId: z.string().uuid().optional().nullable(),
|
||||
issueId: z.string().uuid().optional().nullable(),
|
||||
projectId: z.string().uuid().optional().nullable(),
|
||||
goalId: z.string().uuid().optional().nullable(),
|
||||
heartbeatRunId: z.string().uuid().optional().nullable(),
|
||||
costEventId: z.string().uuid().optional().nullable(),
|
||||
billingCode: z.string().optional().nullable(),
|
||||
description: z.string().max(500).optional().nullable(),
|
||||
eventKind: z.enum(FINANCE_EVENT_KINDS),
|
||||
direction: z.enum(FINANCE_DIRECTIONS).optional().default("debit"),
|
||||
biller: z.string().min(1),
|
||||
provider: z.string().min(1).optional().nullable(),
|
||||
executionAdapterType: z.enum(AGENT_ADAPTER_TYPES).optional().nullable(),
|
||||
pricingTier: z.string().min(1).optional().nullable(),
|
||||
region: z.string().min(1).optional().nullable(),
|
||||
model: z.string().min(1).optional().nullable(),
|
||||
quantity: z.number().int().nonnegative().optional().nullable(),
|
||||
unit: z.enum(FINANCE_UNITS).optional().nullable(),
|
||||
amountCents: z.number().int().nonnegative(),
|
||||
currency: z.string().length(3).optional().default("USD"),
|
||||
estimated: z.boolean().optional().default(false),
|
||||
externalInvoiceId: z.string().optional().nullable(),
|
||||
metadataJson: z.record(z.string(), z.unknown()).optional().nullable(),
|
||||
occurredAt: z.string().datetime(),
|
||||
}).transform((value) => ({
|
||||
...value,
|
||||
currency: value.currency.toUpperCase(),
|
||||
}));
|
||||
|
||||
export type CreateFinanceEvent = z.infer<typeof createFinanceEventSchema>;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { z } from "zod";
|
||||
import { GOAL_LEVELS, GOAL_STATUSES } from "../constants.js";
|
||||
|
||||
export const createGoalSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
description: z.string().optional().nullable(),
|
||||
level: z.enum(GOAL_LEVELS).optional().default("task"),
|
||||
status: z.enum(GOAL_STATUSES).optional().default("planned"),
|
||||
parentId: z.string().uuid().optional().nullable(),
|
||||
ownerAgentId: z.string().uuid().optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateGoal = z.infer<typeof createGoalSchema>;
|
||||
|
||||
export const updateGoalSchema = createGoalSchema.partial();
|
||||
|
||||
export type UpdateGoal = z.infer<typeof updateGoalSchema>;
|
||||
@@ -0,0 +1,281 @@
|
||||
export {
|
||||
instanceGeneralSettingsSchema,
|
||||
patchInstanceGeneralSettingsSchema,
|
||||
type InstanceGeneralSettings,
|
||||
type PatchInstanceGeneralSettings,
|
||||
instanceExperimentalSettingsSchema,
|
||||
patchInstanceExperimentalSettingsSchema,
|
||||
type InstanceExperimentalSettings,
|
||||
type PatchInstanceExperimentalSettings,
|
||||
} from "./instance.js";
|
||||
|
||||
export {
|
||||
upsertBudgetPolicySchema,
|
||||
resolveBudgetIncidentSchema,
|
||||
type UpsertBudgetPolicy,
|
||||
type ResolveBudgetIncident,
|
||||
} from "./budget.js";
|
||||
|
||||
export {
|
||||
createCompanySchema,
|
||||
updateCompanySchema,
|
||||
updateCompanyBrandingSchema,
|
||||
type CreateCompany,
|
||||
type UpdateCompany,
|
||||
type UpdateCompanyBranding,
|
||||
} from "./company.js";
|
||||
export {
|
||||
companySkillSourceTypeSchema,
|
||||
companySkillTrustLevelSchema,
|
||||
companySkillCompatibilitySchema,
|
||||
companySkillSourceBadgeSchema,
|
||||
companySkillFileInventoryEntrySchema,
|
||||
companySkillSchema,
|
||||
companySkillListItemSchema,
|
||||
companySkillUsageAgentSchema,
|
||||
companySkillDetailSchema,
|
||||
companySkillUpdateStatusSchema,
|
||||
companySkillImportSchema,
|
||||
companySkillProjectScanRequestSchema,
|
||||
companySkillProjectScanSkippedSchema,
|
||||
companySkillProjectScanConflictSchema,
|
||||
companySkillProjectScanResultSchema,
|
||||
companySkillCreateSchema,
|
||||
companySkillFileDetailSchema,
|
||||
companySkillFileUpdateSchema,
|
||||
type CompanySkillImport,
|
||||
type CompanySkillProjectScan,
|
||||
type CompanySkillCreate,
|
||||
type CompanySkillFileUpdate,
|
||||
} from "./company-skill.js";
|
||||
export {
|
||||
agentSkillStateSchema,
|
||||
agentSkillSyncModeSchema,
|
||||
agentSkillEntrySchema,
|
||||
agentSkillSnapshotSchema,
|
||||
agentSkillSyncSchema,
|
||||
type AgentSkillSync,
|
||||
} from "./adapter-skills.js";
|
||||
export {
|
||||
portabilityIncludeSchema,
|
||||
portabilityEnvInputSchema,
|
||||
portabilityCompanyManifestEntrySchema,
|
||||
portabilitySidebarOrderSchema,
|
||||
portabilityAgentManifestEntrySchema,
|
||||
portabilitySkillManifestEntrySchema,
|
||||
portabilityManifestSchema,
|
||||
portabilitySourceSchema,
|
||||
portabilityTargetSchema,
|
||||
portabilityAgentSelectionSchema,
|
||||
portabilityCollisionStrategySchema,
|
||||
companyPortabilityExportSchema,
|
||||
companyPortabilityPreviewSchema,
|
||||
companyPortabilityImportSchema,
|
||||
type CompanyPortabilityExport,
|
||||
type CompanyPortabilityPreview,
|
||||
type CompanyPortabilityImport,
|
||||
} from "./company-portability.js";
|
||||
|
||||
export {
|
||||
createAgentSchema,
|
||||
createAgentHireSchema,
|
||||
updateAgentSchema,
|
||||
agentInstructionsBundleModeSchema,
|
||||
updateAgentInstructionsBundleSchema,
|
||||
upsertAgentInstructionsFileSchema,
|
||||
updateAgentInstructionsPathSchema,
|
||||
createAgentKeySchema,
|
||||
wakeAgentSchema,
|
||||
resetAgentSessionSchema,
|
||||
testAdapterEnvironmentSchema,
|
||||
agentPermissionsSchema,
|
||||
updateAgentPermissionsSchema,
|
||||
type CreateAgent,
|
||||
type CreateAgentHire,
|
||||
type UpdateAgent,
|
||||
type UpdateAgentInstructionsBundle,
|
||||
type UpsertAgentInstructionsFile,
|
||||
type UpdateAgentInstructionsPath,
|
||||
type CreateAgentKey,
|
||||
type WakeAgent,
|
||||
type ResetAgentSession,
|
||||
type TestAdapterEnvironment,
|
||||
type UpdateAgentPermissions,
|
||||
} from "./agent.js";
|
||||
|
||||
export {
|
||||
createProjectSchema,
|
||||
updateProjectSchema,
|
||||
createProjectWorkspaceSchema,
|
||||
updateProjectWorkspaceSchema,
|
||||
projectExecutionWorkspacePolicySchema,
|
||||
type CreateProject,
|
||||
type UpdateProject,
|
||||
type CreateProjectWorkspace,
|
||||
type UpdateProjectWorkspace,
|
||||
type ProjectExecutionWorkspacePolicy,
|
||||
} from "./project.js";
|
||||
|
||||
export {
|
||||
createIssueSchema,
|
||||
createIssueLabelSchema,
|
||||
updateIssueSchema,
|
||||
issueExecutionWorkspaceSettingsSchema,
|
||||
checkoutIssueSchema,
|
||||
addIssueCommentSchema,
|
||||
linkIssueApprovalSchema,
|
||||
createIssueAttachmentMetadataSchema,
|
||||
issueDocumentFormatSchema,
|
||||
issueDocumentKeySchema,
|
||||
upsertIssueDocumentSchema,
|
||||
type CreateIssue,
|
||||
type CreateIssueLabel,
|
||||
type UpdateIssue,
|
||||
type IssueExecutionWorkspaceSettings,
|
||||
type CheckoutIssue,
|
||||
type AddIssueComment,
|
||||
type LinkIssueApproval,
|
||||
type CreateIssueAttachmentMetadata,
|
||||
type IssueDocumentFormat,
|
||||
type UpsertIssueDocument,
|
||||
} from "./issue.js";
|
||||
|
||||
export {
|
||||
createIssueWorkProductSchema,
|
||||
updateIssueWorkProductSchema,
|
||||
issueWorkProductTypeSchema,
|
||||
issueWorkProductStatusSchema,
|
||||
issueWorkProductReviewStateSchema,
|
||||
type CreateIssueWorkProduct,
|
||||
type UpdateIssueWorkProduct,
|
||||
} from "./work-product.js";
|
||||
|
||||
export {
|
||||
updateExecutionWorkspaceSchema,
|
||||
executionWorkspaceStatusSchema,
|
||||
type UpdateExecutionWorkspace,
|
||||
} from "./execution-workspace.js";
|
||||
|
||||
export {
|
||||
createGoalSchema,
|
||||
updateGoalSchema,
|
||||
type CreateGoal,
|
||||
type UpdateGoal,
|
||||
} from "./goal.js";
|
||||
|
||||
export {
|
||||
createApprovalSchema,
|
||||
resolveApprovalSchema,
|
||||
requestApprovalRevisionSchema,
|
||||
resubmitApprovalSchema,
|
||||
addApprovalCommentSchema,
|
||||
type CreateApproval,
|
||||
type ResolveApproval,
|
||||
type RequestApprovalRevision,
|
||||
type ResubmitApproval,
|
||||
type AddApprovalComment,
|
||||
} from "./approval.js";
|
||||
|
||||
export {
|
||||
envBindingPlainSchema,
|
||||
envBindingSecretRefSchema,
|
||||
envBindingSchema,
|
||||
envConfigSchema,
|
||||
createSecretSchema,
|
||||
rotateSecretSchema,
|
||||
updateSecretSchema,
|
||||
type CreateSecret,
|
||||
type RotateSecret,
|
||||
type UpdateSecret,
|
||||
} from "./secret.js";
|
||||
|
||||
export {
|
||||
createRoutineSchema,
|
||||
updateRoutineSchema,
|
||||
createRoutineTriggerSchema,
|
||||
updateRoutineTriggerSchema,
|
||||
runRoutineSchema,
|
||||
rotateRoutineTriggerSecretSchema,
|
||||
type CreateRoutine,
|
||||
type UpdateRoutine,
|
||||
type CreateRoutineTrigger,
|
||||
type UpdateRoutineTrigger,
|
||||
type RunRoutine,
|
||||
type RotateRoutineTriggerSecret,
|
||||
} from "./routine.js";
|
||||
|
||||
export {
|
||||
createCostEventSchema,
|
||||
updateBudgetSchema,
|
||||
type CreateCostEvent,
|
||||
type UpdateBudget,
|
||||
} from "./cost.js";
|
||||
|
||||
export {
|
||||
createFinanceEventSchema,
|
||||
type CreateFinanceEvent,
|
||||
} from "./finance.js";
|
||||
|
||||
export {
|
||||
createAssetImageMetadataSchema,
|
||||
type CreateAssetImageMetadata,
|
||||
} from "./asset.js";
|
||||
|
||||
export {
|
||||
createCompanyInviteSchema,
|
||||
createOpenClawInvitePromptSchema,
|
||||
acceptInviteSchema,
|
||||
listJoinRequestsQuerySchema,
|
||||
claimJoinRequestApiKeySchema,
|
||||
boardCliAuthAccessLevelSchema,
|
||||
createCliAuthChallengeSchema,
|
||||
resolveCliAuthChallengeSchema,
|
||||
updateMemberPermissionsSchema,
|
||||
updateUserCompanyAccessSchema,
|
||||
type CreateCompanyInvite,
|
||||
type CreateOpenClawInvitePrompt,
|
||||
type AcceptInvite,
|
||||
type ListJoinRequestsQuery,
|
||||
type ClaimJoinRequestApiKey,
|
||||
type BoardCliAuthAccessLevel,
|
||||
type CreateCliAuthChallenge,
|
||||
type ResolveCliAuthChallenge,
|
||||
type UpdateMemberPermissions,
|
||||
type UpdateUserCompanyAccess,
|
||||
} from "./access.js";
|
||||
|
||||
export {
|
||||
jsonSchemaSchema,
|
||||
pluginJobDeclarationSchema,
|
||||
pluginWebhookDeclarationSchema,
|
||||
pluginToolDeclarationSchema,
|
||||
pluginUiSlotDeclarationSchema,
|
||||
pluginLauncherActionDeclarationSchema,
|
||||
pluginLauncherRenderDeclarationSchema,
|
||||
pluginLauncherDeclarationSchema,
|
||||
pluginManifestV1Schema,
|
||||
installPluginSchema,
|
||||
upsertPluginConfigSchema,
|
||||
patchPluginConfigSchema,
|
||||
updatePluginStatusSchema,
|
||||
uninstallPluginSchema,
|
||||
pluginStateScopeKeySchema,
|
||||
setPluginStateSchema,
|
||||
listPluginStateSchema,
|
||||
type PluginJobDeclarationInput,
|
||||
type PluginWebhookDeclarationInput,
|
||||
type PluginToolDeclarationInput,
|
||||
type PluginUiSlotDeclarationInput,
|
||||
type PluginLauncherActionDeclarationInput,
|
||||
type PluginLauncherRenderDeclarationInput,
|
||||
type PluginLauncherDeclarationInput,
|
||||
type PluginManifestV1Input,
|
||||
type InstallPlugin,
|
||||
type UpsertPluginConfig,
|
||||
type PatchPluginConfig,
|
||||
type UpdatePluginStatus,
|
||||
type UninstallPlugin,
|
||||
type PluginStateScopeKey,
|
||||
type SetPluginState,
|
||||
type ListPluginState,
|
||||
} from "./plugin.js";
|
||||
@@ -0,0 +1,19 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const instanceGeneralSettingsSchema = z.object({
|
||||
censorUsernameInLogs: z.boolean().default(false),
|
||||
}).strict();
|
||||
|
||||
export const patchInstanceGeneralSettingsSchema = instanceGeneralSettingsSchema.partial();
|
||||
|
||||
export const instanceExperimentalSettingsSchema = z.object({
|
||||
enableIsolatedWorkspaces: z.boolean().default(false),
|
||||
autoRestartDevServerWhenIdle: z.boolean().default(false),
|
||||
}).strict();
|
||||
|
||||
export const patchInstanceExperimentalSettingsSchema = instanceExperimentalSettingsSchema.partial();
|
||||
|
||||
export type InstanceGeneralSettings = z.infer<typeof instanceGeneralSettingsSchema>;
|
||||
export type PatchInstanceGeneralSettings = z.infer<typeof patchInstanceGeneralSettingsSchema>;
|
||||
export type InstanceExperimentalSettings = z.infer<typeof instanceExperimentalSettingsSchema>;
|
||||
export type PatchInstanceExperimentalSettings = z.infer<typeof patchInstanceExperimentalSettingsSchema>;
|
||||
@@ -0,0 +1,122 @@
|
||||
import { z } from "zod";
|
||||
import { ISSUE_PRIORITIES, ISSUE_STATUSES } from "../constants.js";
|
||||
|
||||
const executionWorkspaceStrategySchema = z
|
||||
.object({
|
||||
type: z.enum(["project_primary", "git_worktree", "adapter_managed", "cloud_sandbox"]).optional(),
|
||||
baseRef: z.string().optional().nullable(),
|
||||
branchTemplate: z.string().optional().nullable(),
|
||||
worktreeParentDir: z.string().optional().nullable(),
|
||||
provisionCommand: z.string().optional().nullable(),
|
||||
teardownCommand: z.string().optional().nullable(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const issueExecutionWorkspaceSettingsSchema = z
|
||||
.object({
|
||||
mode: z.enum(["inherit", "shared_workspace", "isolated_workspace", "operator_branch", "reuse_existing", "agent_default"]).optional(),
|
||||
workspaceStrategy: executionWorkspaceStrategySchema.optional().nullable(),
|
||||
workspaceRuntime: z.record(z.unknown()).optional().nullable(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const issueAssigneeAdapterOverridesSchema = z
|
||||
.object({
|
||||
adapterConfig: z.record(z.unknown()).optional(),
|
||||
useProjectWorkspace: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const createIssueSchema = z.object({
|
||||
projectId: z.string().uuid().optional().nullable(),
|
||||
projectWorkspaceId: z.string().uuid().optional().nullable(),
|
||||
goalId: z.string().uuid().optional().nullable(),
|
||||
parentId: z.string().uuid().optional().nullable(),
|
||||
title: z.string().min(1),
|
||||
description: z.string().optional().nullable(),
|
||||
status: z.enum(ISSUE_STATUSES).optional().default("backlog"),
|
||||
priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"),
|
||||
assigneeAgentId: z.string().uuid().optional().nullable(),
|
||||
assigneeUserId: z.string().optional().nullable(),
|
||||
requestDepth: z.number().int().nonnegative().optional().default(0),
|
||||
billingCode: z.string().optional().nullable(),
|
||||
assigneeAdapterOverrides: issueAssigneeAdapterOverridesSchema.optional().nullable(),
|
||||
executionWorkspaceId: z.string().uuid().optional().nullable(),
|
||||
executionWorkspacePreference: z.enum([
|
||||
"inherit",
|
||||
"shared_workspace",
|
||||
"isolated_workspace",
|
||||
"operator_branch",
|
||||
"reuse_existing",
|
||||
"agent_default",
|
||||
]).optional().nullable(),
|
||||
executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(),
|
||||
labelIds: z.array(z.string().uuid()).optional(),
|
||||
});
|
||||
|
||||
export type CreateIssue = z.infer<typeof createIssueSchema>;
|
||||
|
||||
export const createIssueLabelSchema = z.object({
|
||||
name: z.string().trim().min(1).max(48),
|
||||
color: z.string().regex(/^#(?:[0-9a-fA-F]{6})$/, "Color must be a 6-digit hex value"),
|
||||
});
|
||||
|
||||
export type CreateIssueLabel = z.infer<typeof createIssueLabelSchema>;
|
||||
|
||||
export const updateIssueSchema = createIssueSchema.partial().extend({
|
||||
comment: z.string().min(1).optional(),
|
||||
reopen: z.boolean().optional(),
|
||||
hiddenAt: z.string().datetime().nullable().optional(),
|
||||
});
|
||||
|
||||
export type UpdateIssue = z.infer<typeof updateIssueSchema>;
|
||||
export type IssueExecutionWorkspaceSettings = z.infer<typeof issueExecutionWorkspaceSettingsSchema>;
|
||||
|
||||
export const checkoutIssueSchema = z.object({
|
||||
agentId: z.string().uuid(),
|
||||
expectedStatuses: z.array(z.enum(ISSUE_STATUSES)).nonempty(),
|
||||
});
|
||||
|
||||
export type CheckoutIssue = z.infer<typeof checkoutIssueSchema>;
|
||||
|
||||
export const addIssueCommentSchema = z.object({
|
||||
body: z.string().min(1),
|
||||
reopen: z.boolean().optional(),
|
||||
interrupt: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type AddIssueComment = z.infer<typeof addIssueCommentSchema>;
|
||||
|
||||
export const linkIssueApprovalSchema = z.object({
|
||||
approvalId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export type LinkIssueApproval = z.infer<typeof linkIssueApprovalSchema>;
|
||||
|
||||
export const createIssueAttachmentMetadataSchema = z.object({
|
||||
issueCommentId: z.string().uuid().optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateIssueAttachmentMetadata = z.infer<typeof createIssueAttachmentMetadataSchema>;
|
||||
|
||||
export const ISSUE_DOCUMENT_FORMATS = ["markdown"] as const;
|
||||
|
||||
export const issueDocumentFormatSchema = z.enum(ISSUE_DOCUMENT_FORMATS);
|
||||
|
||||
export const issueDocumentKeySchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(64)
|
||||
.regex(/^[a-z0-9][a-z0-9_-]*$/, "Document key must be lowercase letters, numbers, _ or -");
|
||||
|
||||
export const upsertIssueDocumentSchema = z.object({
|
||||
title: z.string().trim().max(200).nullable().optional(),
|
||||
format: issueDocumentFormatSchema,
|
||||
body: z.string().max(524288),
|
||||
changeSummary: z.string().trim().max(500).nullable().optional(),
|
||||
baseRevisionId: z.string().uuid().nullable().optional(),
|
||||
});
|
||||
|
||||
export type IssueDocumentFormat = z.infer<typeof issueDocumentFormatSchema>;
|
||||
export type UpsertIssueDocument = z.infer<typeof upsertIssueDocumentSchema>;
|
||||
@@ -0,0 +1,670 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
PLUGIN_STATUSES,
|
||||
PLUGIN_CATEGORIES,
|
||||
PLUGIN_CAPABILITIES,
|
||||
PLUGIN_UI_SLOT_TYPES,
|
||||
PLUGIN_UI_SLOT_ENTITY_TYPES,
|
||||
PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS,
|
||||
PLUGIN_LAUNCHER_PLACEMENT_ZONES,
|
||||
PLUGIN_LAUNCHER_ACTIONS,
|
||||
PLUGIN_LAUNCHER_BOUNDS,
|
||||
PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS,
|
||||
PLUGIN_STATE_SCOPE_KINDS,
|
||||
} from "../constants.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JSON Schema placeholder – a permissive validator for JSON Schema objects
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Permissive validator for JSON Schema objects. Accepts any `Record<string, unknown>`
|
||||
* that contains at least a `type`, `$ref`, or composition keyword (`oneOf`/`anyOf`/`allOf`).
|
||||
* Empty objects are also accepted.
|
||||
*
|
||||
* Used to validate `instanceConfigSchema` and `parametersSchema` fields in the
|
||||
* plugin manifest without fully parsing JSON Schema.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §10.1 — Manifest shape
|
||||
*/
|
||||
export const jsonSchemaSchema = z.record(z.unknown()).refine(
|
||||
(val) => {
|
||||
// Must have a "type" field if non-empty, or be a valid JSON Schema object
|
||||
if (Object.keys(val).length === 0) return true;
|
||||
return typeof val.type === "string" || val.$ref !== undefined || val.oneOf !== undefined || val.anyOf !== undefined || val.allOf !== undefined;
|
||||
},
|
||||
{ message: "Must be a valid JSON Schema object (requires at least a 'type', '$ref', or composition keyword)" },
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Manifest sub-type schemas
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validates a {@link PluginJobDeclaration} — a scheduled job declared in the
|
||||
* plugin manifest. Requires `jobKey` and `displayName`; `description` and
|
||||
* `schedule` (cron expression) are optional.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §17 — Scheduled Jobs
|
||||
*/
|
||||
/**
|
||||
* Validates a cron expression has exactly 5 whitespace-separated fields,
|
||||
* each containing only valid cron characters (digits, *, /, -, ,).
|
||||
*
|
||||
* Valid tokens per field: *, N, N-M, N/S, * /S, N-M/S, and comma-separated lists.
|
||||
*/
|
||||
const CRON_FIELD_PATTERN = /^(\*(?:\/[0-9]+)?|[0-9]+(?:-[0-9]+)?(?:\/[0-9]+)?)(?:,(\*(?:\/[0-9]+)?|[0-9]+(?:-[0-9]+)?(?:\/[0-9]+)?))*$/;
|
||||
|
||||
function isValidCronExpression(expression: string): boolean {
|
||||
const trimmed = expression.trim();
|
||||
if (!trimmed) return false;
|
||||
const fields = trimmed.split(/\s+/);
|
||||
if (fields.length !== 5) return false;
|
||||
return fields.every((f) => CRON_FIELD_PATTERN.test(f));
|
||||
}
|
||||
|
||||
export const pluginJobDeclarationSchema = z.object({
|
||||
jobKey: z.string().min(1),
|
||||
displayName: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
schedule: z.string().refine(
|
||||
(val) => isValidCronExpression(val),
|
||||
{ message: "schedule must be a valid 5-field cron expression (e.g. '*/15 * * * *')" },
|
||||
).optional(),
|
||||
});
|
||||
|
||||
export type PluginJobDeclarationInput = z.infer<typeof pluginJobDeclarationSchema>;
|
||||
|
||||
/**
|
||||
* Validates a {@link PluginWebhookDeclaration} — a webhook endpoint declared
|
||||
* in the plugin manifest. Requires `endpointKey` and `displayName`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §18 — Webhooks
|
||||
*/
|
||||
export const pluginWebhookDeclarationSchema = z.object({
|
||||
endpointKey: z.string().min(1),
|
||||
displayName: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export type PluginWebhookDeclarationInput = z.infer<typeof pluginWebhookDeclarationSchema>;
|
||||
|
||||
/**
|
||||
* Validates a {@link PluginToolDeclaration} — an agent tool contributed by the
|
||||
* plugin. Requires `name`, `displayName`, `description`, and a valid
|
||||
* `parametersSchema`. Requires the `agent.tools.register` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §11 — Agent Tools
|
||||
*/
|
||||
export const pluginToolDeclarationSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
displayName: z.string().min(1),
|
||||
description: z.string().min(1),
|
||||
parametersSchema: jsonSchemaSchema,
|
||||
});
|
||||
|
||||
export type PluginToolDeclarationInput = z.infer<typeof pluginToolDeclarationSchema>;
|
||||
|
||||
/**
|
||||
* Validates a {@link PluginUiSlotDeclaration} — a UI extension slot the plugin
|
||||
* fills with a React component. Includes `superRefine` checks for slot-specific
|
||||
* requirements such as `entityTypes` for context-sensitive slots.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
*/
|
||||
export const pluginUiSlotDeclarationSchema = z.object({
|
||||
type: z.enum(PLUGIN_UI_SLOT_TYPES),
|
||||
id: z.string().min(1),
|
||||
displayName: z.string().min(1),
|
||||
exportName: z.string().min(1),
|
||||
entityTypes: z.array(z.enum(PLUGIN_UI_SLOT_ENTITY_TYPES)).optional(),
|
||||
routePath: z.string().regex(/^[a-z0-9][a-z0-9-]*$/, {
|
||||
message: "routePath must be a lowercase single-segment slug (letters, numbers, hyphens)",
|
||||
}).optional(),
|
||||
order: z.number().int().optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
// context-sensitive slots require explicit entity targeting.
|
||||
const entityScopedTypes = ["detailTab", "taskDetailView", "contextMenuItem", "commentAnnotation", "commentContextMenuItem", "projectSidebarItem"];
|
||||
if (
|
||||
entityScopedTypes.includes(value.type)
|
||||
&& (!value.entityTypes || value.entityTypes.length === 0)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `${value.type} slots require at least one entityType`,
|
||||
path: ["entityTypes"],
|
||||
});
|
||||
}
|
||||
// projectSidebarItem only makes sense for entityType "project".
|
||||
if (value.type === "projectSidebarItem" && value.entityTypes && !value.entityTypes.includes("project")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "projectSidebarItem slots require entityTypes to include \"project\"",
|
||||
path: ["entityTypes"],
|
||||
});
|
||||
}
|
||||
// commentAnnotation only makes sense for entityType "comment".
|
||||
if (value.type === "commentAnnotation" && value.entityTypes && !value.entityTypes.includes("comment")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "commentAnnotation slots require entityTypes to include \"comment\"",
|
||||
path: ["entityTypes"],
|
||||
});
|
||||
}
|
||||
// commentContextMenuItem only makes sense for entityType "comment".
|
||||
if (value.type === "commentContextMenuItem" && value.entityTypes && !value.entityTypes.includes("comment")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "commentContextMenuItem slots require entityTypes to include \"comment\"",
|
||||
path: ["entityTypes"],
|
||||
});
|
||||
}
|
||||
if (value.routePath && value.type !== "page") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "routePath is only supported for page slots",
|
||||
path: ["routePath"],
|
||||
});
|
||||
}
|
||||
if (value.routePath && PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS.includes(value.routePath as (typeof PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS)[number])) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `routePath "${value.routePath}" is reserved by the host`,
|
||||
path: ["routePath"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type PluginUiSlotDeclarationInput = z.infer<typeof pluginUiSlotDeclarationSchema>;
|
||||
|
||||
const entityScopedLauncherPlacementZones = [
|
||||
"detailTab",
|
||||
"taskDetailView",
|
||||
"contextMenuItem",
|
||||
"commentAnnotation",
|
||||
"commentContextMenuItem",
|
||||
"projectSidebarItem",
|
||||
] as const;
|
||||
|
||||
const launcherBoundsByEnvironment: Record<
|
||||
(typeof PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS)[number],
|
||||
readonly (typeof PLUGIN_LAUNCHER_BOUNDS)[number][]
|
||||
> = {
|
||||
hostInline: ["inline", "compact", "default"],
|
||||
hostOverlay: ["compact", "default", "wide", "full"],
|
||||
hostRoute: ["default", "wide", "full"],
|
||||
external: [],
|
||||
iframe: ["compact", "default", "wide", "full"],
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the action payload for a declarative plugin launcher.
|
||||
*/
|
||||
export const pluginLauncherActionDeclarationSchema = z.object({
|
||||
type: z.enum(PLUGIN_LAUNCHER_ACTIONS),
|
||||
target: z.string().min(1),
|
||||
params: z.record(z.unknown()).optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (value.type === "performAction" && value.target.includes("/")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "performAction launchers must target an action key, not a route or URL",
|
||||
path: ["target"],
|
||||
});
|
||||
}
|
||||
|
||||
if (value.type === "navigate" && /^https?:\/\//.test(value.target)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "navigate launchers must target a host route, not an absolute URL",
|
||||
path: ["target"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type PluginLauncherActionDeclarationInput =
|
||||
z.infer<typeof pluginLauncherActionDeclarationSchema>;
|
||||
|
||||
/**
|
||||
* Validates optional render hints for a plugin launcher destination.
|
||||
*/
|
||||
export const pluginLauncherRenderDeclarationSchema = z.object({
|
||||
environment: z.enum(PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS),
|
||||
bounds: z.enum(PLUGIN_LAUNCHER_BOUNDS).optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (!value.bounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
const supportedBounds = launcherBoundsByEnvironment[value.environment];
|
||||
if (!supportedBounds.includes(value.bounds)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `bounds "${value.bounds}" is not supported for render environment "${value.environment}"`,
|
||||
path: ["bounds"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type PluginLauncherRenderDeclarationInput =
|
||||
z.infer<typeof pluginLauncherRenderDeclarationSchema>;
|
||||
|
||||
/**
|
||||
* Validates declarative launcher metadata in a plugin manifest.
|
||||
*/
|
||||
export const pluginLauncherDeclarationSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
displayName: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
placementZone: z.enum(PLUGIN_LAUNCHER_PLACEMENT_ZONES),
|
||||
exportName: z.string().min(1).optional(),
|
||||
entityTypes: z.array(z.enum(PLUGIN_UI_SLOT_ENTITY_TYPES)).optional(),
|
||||
order: z.number().int().optional(),
|
||||
action: pluginLauncherActionDeclarationSchema,
|
||||
render: pluginLauncherRenderDeclarationSchema.optional(),
|
||||
}).superRefine((value, ctx) => {
|
||||
if (
|
||||
entityScopedLauncherPlacementZones.some((zone) => zone === value.placementZone)
|
||||
&& (!value.entityTypes || value.entityTypes.length === 0)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `${value.placementZone} launchers require at least one entityType`,
|
||||
path: ["entityTypes"],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
value.placementZone === "projectSidebarItem"
|
||||
&& value.entityTypes
|
||||
&& !value.entityTypes.includes("project")
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "projectSidebarItem launchers require entityTypes to include \"project\"",
|
||||
path: ["entityTypes"],
|
||||
});
|
||||
}
|
||||
|
||||
if (value.action.type === "performAction" && value.render) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "performAction launchers cannot declare render hints",
|
||||
path: ["render"],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
["openModal", "openDrawer", "openPopover"].includes(value.action.type)
|
||||
&& !value.render
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `${value.action.type} launchers require render metadata`,
|
||||
path: ["render"],
|
||||
});
|
||||
}
|
||||
|
||||
if (value.action.type === "openModal" && value.render?.environment === "hostInline") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "openModal launchers cannot use the hostInline render environment",
|
||||
path: ["render", "environment"],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
value.action.type === "openDrawer"
|
||||
&& value.render
|
||||
&& !["hostOverlay", "iframe"].includes(value.render.environment)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "openDrawer launchers must use hostOverlay or iframe render environments",
|
||||
path: ["render", "environment"],
|
||||
});
|
||||
}
|
||||
|
||||
if (value.action.type === "openPopover" && value.render?.environment === "hostRoute") {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "openPopover launchers cannot use the hostRoute render environment",
|
||||
path: ["render", "environment"],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export type PluginLauncherDeclarationInput = z.infer<typeof pluginLauncherDeclarationSchema>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin Manifest V1 schema
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Zod schema for {@link PaperclipPluginManifestV1} — the complete runtime
|
||||
* validator for plugin manifests read at install time.
|
||||
*
|
||||
* Field-level constraints (see PLUGIN_SPEC.md §10.1 for the normative rules):
|
||||
*
|
||||
* | Field | Type | Constraints |
|
||||
* |--------------------------|------------|----------------------------------------------|
|
||||
* | `id` | string | `^[a-z0-9][a-z0-9._-]*$` |
|
||||
* | `apiVersion` | literal 1 | must equal `PLUGIN_API_VERSION` |
|
||||
* | `version` | string | semver (`\d+\.\d+\.\d+`) |
|
||||
* | `displayName` | string | 1–100 chars |
|
||||
* | `description` | string | 1–500 chars |
|
||||
* | `author` | string | 1–200 chars |
|
||||
* | `categories` | enum[] | at least one; values from PLUGIN_CATEGORIES |
|
||||
* | `minimumHostVersion` | string? | semver lower bound if present, no leading `v`|
|
||||
* | `minimumPaperclipVersion`| string? | legacy alias of `minimumHostVersion` |
|
||||
* | `capabilities` | enum[] | at least one; values from PLUGIN_CAPABILITIES|
|
||||
* | `entrypoints.worker` | string | min 1 char |
|
||||
* | `entrypoints.ui` | string? | required when `ui.slots` is declared |
|
||||
*
|
||||
* Cross-field rules enforced via `superRefine`:
|
||||
* - `entrypoints.ui` required when `ui.slots` declared
|
||||
* - `agent.tools.register` capability required when `tools` declared
|
||||
* - `jobs.schedule` capability required when `jobs` declared
|
||||
* - `webhooks.receive` capability required when `webhooks` declared
|
||||
* - duplicate `jobs[].jobKey` values are rejected
|
||||
* - duplicate `webhooks[].endpointKey` values are rejected
|
||||
* - duplicate `tools[].name` values are rejected
|
||||
* - duplicate `ui.slots[].id` values are rejected
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §10.1 — Manifest shape
|
||||
* @see {@link PaperclipPluginManifestV1} — the inferred TypeScript type
|
||||
*/
|
||||
export const pluginManifestV1Schema = z.object({
|
||||
id: z.string().min(1).regex(
|
||||
/^[a-z0-9][a-z0-9._-]*$/,
|
||||
"Plugin id must start with a lowercase alphanumeric and contain only lowercase letters, digits, dots, hyphens, or underscores",
|
||||
),
|
||||
apiVersion: z.literal(1),
|
||||
version: z.string().min(1).regex(
|
||||
/^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/,
|
||||
"Version must follow semver (e.g. 1.0.0 or 1.0.0-beta.1)",
|
||||
),
|
||||
displayName: z.string().min(1).max(100),
|
||||
description: z.string().min(1).max(500),
|
||||
author: z.string().min(1).max(200),
|
||||
categories: z.array(z.enum(PLUGIN_CATEGORIES)).min(1),
|
||||
minimumHostVersion: z.string().regex(
|
||||
/^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/,
|
||||
"minimumHostVersion must follow semver (e.g. 1.0.0)",
|
||||
).optional(),
|
||||
minimumPaperclipVersion: z.string().regex(
|
||||
/^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/,
|
||||
"minimumPaperclipVersion must follow semver (e.g. 1.0.0)",
|
||||
).optional(),
|
||||
capabilities: z.array(z.enum(PLUGIN_CAPABILITIES)).min(1),
|
||||
entrypoints: z.object({
|
||||
worker: z.string().min(1),
|
||||
ui: z.string().min(1).optional(),
|
||||
}),
|
||||
instanceConfigSchema: jsonSchemaSchema.optional(),
|
||||
jobs: z.array(pluginJobDeclarationSchema).optional(),
|
||||
webhooks: z.array(pluginWebhookDeclarationSchema).optional(),
|
||||
tools: z.array(pluginToolDeclarationSchema).optional(),
|
||||
launchers: z.array(pluginLauncherDeclarationSchema).optional(),
|
||||
ui: z.object({
|
||||
slots: z.array(pluginUiSlotDeclarationSchema).min(1).optional(),
|
||||
launchers: z.array(pluginLauncherDeclarationSchema).optional(),
|
||||
}).optional(),
|
||||
}).superRefine((manifest, ctx) => {
|
||||
// ── Entrypoint ↔ UI slot consistency ──────────────────────────────────
|
||||
// Plugins that declare UI slots must also declare a UI entrypoint so the
|
||||
// host knows where to load the bundle from (PLUGIN_SPEC.md §10.1).
|
||||
const hasUiSlots = (manifest.ui?.slots?.length ?? 0) > 0;
|
||||
const hasUiLaunchers = (manifest.ui?.launchers?.length ?? 0) > 0;
|
||||
if ((hasUiSlots || hasUiLaunchers) && !manifest.entrypoints.ui) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "entrypoints.ui is required when ui.slots or ui.launchers are declared",
|
||||
path: ["entrypoints", "ui"],
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
manifest.minimumHostVersion
|
||||
&& manifest.minimumPaperclipVersion
|
||||
&& manifest.minimumHostVersion !== manifest.minimumPaperclipVersion
|
||||
) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "minimumHostVersion and minimumPaperclipVersion must match when both are declared",
|
||||
path: ["minimumHostVersion"],
|
||||
});
|
||||
}
|
||||
|
||||
// ── Capability ↔ feature declaration consistency ───────────────────────
|
||||
// The host enforces capabilities at install and runtime. A plugin must
|
||||
// declare every capability it needs up-front; silently having more features
|
||||
// than capabilities would cause runtime rejections.
|
||||
|
||||
// tools require agent.tools.register (PLUGIN_SPEC.md §11)
|
||||
if (manifest.tools && manifest.tools.length > 0) {
|
||||
if (!manifest.capabilities.includes("agent.tools.register")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Capability 'agent.tools.register' is required when tools are declared",
|
||||
path: ["capabilities"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// jobs require jobs.schedule (PLUGIN_SPEC.md §17)
|
||||
if (manifest.jobs && manifest.jobs.length > 0) {
|
||||
if (!manifest.capabilities.includes("jobs.schedule")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Capability 'jobs.schedule' is required when jobs are declared",
|
||||
path: ["capabilities"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// webhooks require webhooks.receive (PLUGIN_SPEC.md §18)
|
||||
if (manifest.webhooks && manifest.webhooks.length > 0) {
|
||||
if (!manifest.capabilities.includes("webhooks.receive")) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Capability 'webhooks.receive' is required when webhooks are declared",
|
||||
path: ["capabilities"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Uniqueness checks ──────────────────────────────────────────────────
|
||||
// Duplicate keys within a plugin's own manifest are always a bug. The host
|
||||
// would not know which declaration takes precedence, so we reject early.
|
||||
|
||||
// job keys must be unique within the plugin (used as identifiers in the DB)
|
||||
if (manifest.jobs) {
|
||||
const jobKeys = manifest.jobs.map((j) => j.jobKey);
|
||||
const duplicates = jobKeys.filter((key, i) => jobKeys.indexOf(key) !== i);
|
||||
if (duplicates.length > 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate job keys: ${[...new Set(duplicates)].join(", ")}`,
|
||||
path: ["jobs"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// webhook endpoint keys must be unique within the plugin (used in routes)
|
||||
if (manifest.webhooks) {
|
||||
const endpointKeys = manifest.webhooks.map((w) => w.endpointKey);
|
||||
const duplicates = endpointKeys.filter((key, i) => endpointKeys.indexOf(key) !== i);
|
||||
if (duplicates.length > 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate webhook endpoint keys: ${[...new Set(duplicates)].join(", ")}`,
|
||||
path: ["webhooks"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// tool names must be unique within the plugin (namespaced at runtime)
|
||||
if (manifest.tools) {
|
||||
const toolNames = manifest.tools.map((t) => t.name);
|
||||
const duplicates = toolNames.filter((name, i) => toolNames.indexOf(name) !== i);
|
||||
if (duplicates.length > 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate tool names: ${[...new Set(duplicates)].join(", ")}`,
|
||||
path: ["tools"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// UI slot ids must be unique within the plugin (namespaced at runtime)
|
||||
if (manifest.ui) {
|
||||
if (manifest.ui.slots) {
|
||||
const slotIds = manifest.ui.slots.map((s) => s.id);
|
||||
const duplicates = slotIds.filter((id, i) => slotIds.indexOf(id) !== i);
|
||||
if (duplicates.length > 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate UI slot ids: ${[...new Set(duplicates)].join(", ")}`,
|
||||
path: ["ui", "slots"],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// launcher ids must be unique within the plugin
|
||||
const allLaunchers = [
|
||||
...(manifest.launchers ?? []),
|
||||
...(manifest.ui?.launchers ?? []),
|
||||
];
|
||||
if (allLaunchers.length > 0) {
|
||||
const launcherIds = allLaunchers.map((launcher) => launcher.id);
|
||||
const duplicates = launcherIds.filter((id, i) => launcherIds.indexOf(id) !== i);
|
||||
if (duplicates.length > 0) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Duplicate launcher ids: ${[...new Set(duplicates)].join(", ")}`,
|
||||
path: manifest.ui?.launchers ? ["ui", "launchers"] : ["launchers"],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export type PluginManifestV1Input = z.infer<typeof pluginManifestV1Schema>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin installation / registration request
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Schema for installing (registering) a plugin.
|
||||
* The server receives the packageName and resolves the manifest from the
|
||||
* installed package.
|
||||
*/
|
||||
export const installPluginSchema = z.object({
|
||||
packageName: z.string().min(1),
|
||||
version: z.string().min(1).optional(),
|
||||
/** Set by loader for local-path installs so the worker can be resolved. */
|
||||
packagePath: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
export type InstallPlugin = z.infer<typeof installPluginSchema>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin config (instance configuration) schemas
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Schema for creating or updating a plugin's instance configuration.
|
||||
* configJson is validated permissively here; runtime validation against
|
||||
* the plugin's instanceConfigSchema is done at the service layer.
|
||||
*/
|
||||
export const upsertPluginConfigSchema = z.object({
|
||||
configJson: z.record(z.unknown()),
|
||||
});
|
||||
|
||||
export type UpsertPluginConfig = z.infer<typeof upsertPluginConfigSchema>;
|
||||
|
||||
/**
|
||||
* Schema for partially updating a plugin's instance configuration.
|
||||
* Allows a partial merge of config values.
|
||||
*/
|
||||
export const patchPluginConfigSchema = z.object({
|
||||
configJson: z.record(z.unknown()),
|
||||
});
|
||||
|
||||
export type PatchPluginConfig = z.infer<typeof patchPluginConfigSchema>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin status update
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Schema for updating a plugin's lifecycle status. Used by the lifecycle
|
||||
* manager to persist state transitions.
|
||||
*
|
||||
* @see {@link PLUGIN_STATUSES} for the valid status values
|
||||
*/
|
||||
export const updatePluginStatusSchema = z.object({
|
||||
status: z.enum(PLUGIN_STATUSES),
|
||||
lastError: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export type UpdatePluginStatus = z.infer<typeof updatePluginStatusSchema>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin uninstall
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Schema for the uninstall request. `removeData` controls hard vs soft delete. */
|
||||
export const uninstallPluginSchema = z.object({
|
||||
removeData: z.boolean().optional().default(false),
|
||||
});
|
||||
|
||||
export type UninstallPlugin = z.infer<typeof uninstallPluginSchema>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Plugin state (key-value storage) schemas
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Schema for a plugin state scope key — identifies the exact location where
|
||||
* state is stored. Used by the `ctx.state.get()`, `ctx.state.set()`, and
|
||||
* `ctx.state.delete()` SDK methods.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_state`
|
||||
*/
|
||||
export const pluginStateScopeKeySchema = z.object({
|
||||
scopeKind: z.enum(PLUGIN_STATE_SCOPE_KINDS),
|
||||
scopeId: z.string().min(1).optional(),
|
||||
namespace: z.string().min(1).optional(),
|
||||
stateKey: z.string().min(1),
|
||||
});
|
||||
|
||||
export type PluginStateScopeKey = z.infer<typeof pluginStateScopeKeySchema>;
|
||||
|
||||
/**
|
||||
* Schema for setting a plugin state value.
|
||||
*/
|
||||
export const setPluginStateSchema = z.object({
|
||||
scopeKind: z.enum(PLUGIN_STATE_SCOPE_KINDS),
|
||||
scopeId: z.string().min(1).optional(),
|
||||
namespace: z.string().min(1).optional(),
|
||||
stateKey: z.string().min(1),
|
||||
/** JSON-serializable value to store. */
|
||||
value: z.unknown(),
|
||||
});
|
||||
|
||||
export type SetPluginState = z.infer<typeof setPluginStateSchema>;
|
||||
|
||||
/**
|
||||
* Schema for querying plugin state entries. All fields are optional to allow
|
||||
* flexible list queries (e.g. all state for a plugin within a scope).
|
||||
*/
|
||||
export const listPluginStateSchema = z.object({
|
||||
scopeKind: z.enum(PLUGIN_STATE_SCOPE_KINDS).optional(),
|
||||
scopeId: z.string().min(1).optional(),
|
||||
namespace: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
export type ListPluginState = z.infer<typeof listPluginStateSchema>;
|
||||
@@ -0,0 +1,114 @@
|
||||
import { z } from "zod";
|
||||
import { PROJECT_STATUSES } from "../constants.js";
|
||||
|
||||
const executionWorkspaceStrategySchema = z
|
||||
.object({
|
||||
type: z.enum(["project_primary", "git_worktree", "adapter_managed", "cloud_sandbox"]).optional(),
|
||||
baseRef: z.string().optional().nullable(),
|
||||
branchTemplate: z.string().optional().nullable(),
|
||||
worktreeParentDir: z.string().optional().nullable(),
|
||||
provisionCommand: z.string().optional().nullable(),
|
||||
teardownCommand: z.string().optional().nullable(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const projectExecutionWorkspacePolicySchema = z
|
||||
.object({
|
||||
enabled: z.boolean(),
|
||||
defaultMode: z.enum(["shared_workspace", "isolated_workspace", "operator_branch", "adapter_default"]).optional(),
|
||||
allowIssueOverride: z.boolean().optional(),
|
||||
defaultProjectWorkspaceId: z.string().uuid().optional().nullable(),
|
||||
workspaceStrategy: executionWorkspaceStrategySchema.optional().nullable(),
|
||||
workspaceRuntime: z.record(z.unknown()).optional().nullable(),
|
||||
branchPolicy: z.record(z.unknown()).optional().nullable(),
|
||||
pullRequestPolicy: z.record(z.unknown()).optional().nullable(),
|
||||
runtimePolicy: z.record(z.unknown()).optional().nullable(),
|
||||
cleanupPolicy: z.record(z.unknown()).optional().nullable(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const projectWorkspaceSourceTypeSchema = z.enum(["local_path", "git_repo", "remote_managed", "non_git_path"]);
|
||||
const projectWorkspaceVisibilitySchema = z.enum(["default", "advanced"]);
|
||||
|
||||
const projectWorkspaceFields = {
|
||||
name: z.string().min(1).optional(),
|
||||
sourceType: projectWorkspaceSourceTypeSchema.optional(),
|
||||
cwd: z.string().min(1).optional().nullable(),
|
||||
repoUrl: z.string().url().optional().nullable(),
|
||||
repoRef: z.string().optional().nullable(),
|
||||
defaultRef: z.string().optional().nullable(),
|
||||
visibility: projectWorkspaceVisibilitySchema.optional(),
|
||||
setupCommand: z.string().optional().nullable(),
|
||||
cleanupCommand: z.string().optional().nullable(),
|
||||
remoteProvider: z.string().optional().nullable(),
|
||||
remoteWorkspaceRef: z.string().optional().nullable(),
|
||||
sharedWorkspaceKey: z.string().optional().nullable(),
|
||||
metadata: z.record(z.unknown()).optional().nullable(),
|
||||
};
|
||||
|
||||
function validateProjectWorkspace(value: Record<string, unknown>, ctx: z.RefinementCtx) {
|
||||
const sourceType = value.sourceType ?? "local_path";
|
||||
const hasCwd = typeof value.cwd === "string" && value.cwd.trim().length > 0;
|
||||
const hasRepo = typeof value.repoUrl === "string" && value.repoUrl.trim().length > 0;
|
||||
const hasRemoteRef = typeof value.remoteWorkspaceRef === "string" && value.remoteWorkspaceRef.trim().length > 0;
|
||||
|
||||
if (sourceType === "remote_managed") {
|
||||
if (!hasRemoteRef && !hasRepo) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Remote-managed workspace requires remoteWorkspaceRef or repoUrl.",
|
||||
path: ["remoteWorkspaceRef"],
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasCwd && !hasRepo) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Workspace requires at least one of cwd or repoUrl.",
|
||||
path: ["cwd"],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const createProjectWorkspaceSchema = z.object({
|
||||
...projectWorkspaceFields,
|
||||
isPrimary: z.boolean().optional().default(false),
|
||||
}).superRefine(validateProjectWorkspace);
|
||||
|
||||
export type CreateProjectWorkspace = z.infer<typeof createProjectWorkspaceSchema>;
|
||||
|
||||
export const updateProjectWorkspaceSchema = z.object({
|
||||
...projectWorkspaceFields,
|
||||
isPrimary: z.boolean().optional(),
|
||||
}).partial();
|
||||
|
||||
export type UpdateProjectWorkspace = z.infer<typeof updateProjectWorkspaceSchema>;
|
||||
|
||||
const projectFields = {
|
||||
/** @deprecated Use goalIds instead */
|
||||
goalId: z.string().uuid().optional().nullable(),
|
||||
goalIds: z.array(z.string().uuid()).optional(),
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional().nullable(),
|
||||
status: z.enum(PROJECT_STATUSES).optional().default("backlog"),
|
||||
leadAgentId: z.string().uuid().optional().nullable(),
|
||||
targetDate: z.string().optional().nullable(),
|
||||
color: z.string().optional().nullable(),
|
||||
executionWorkspacePolicy: projectExecutionWorkspacePolicySchema.optional().nullable(),
|
||||
archivedAt: z.string().datetime().optional().nullable(),
|
||||
};
|
||||
|
||||
export const createProjectSchema = z.object({
|
||||
...projectFields,
|
||||
workspace: createProjectWorkspaceSchema.optional(),
|
||||
});
|
||||
|
||||
export type CreateProject = z.infer<typeof createProjectSchema>;
|
||||
|
||||
export const updateProjectSchema = z.object(projectFields).partial();
|
||||
|
||||
export type UpdateProject = z.infer<typeof updateProjectSchema>;
|
||||
|
||||
export type ProjectExecutionWorkspacePolicy = z.infer<typeof projectExecutionWorkspacePolicySchema>;
|
||||
@@ -0,0 +1,72 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
ISSUE_PRIORITIES,
|
||||
ROUTINE_CATCH_UP_POLICIES,
|
||||
ROUTINE_CONCURRENCY_POLICIES,
|
||||
ROUTINE_STATUSES,
|
||||
ROUTINE_TRIGGER_SIGNING_MODES,
|
||||
} from "../constants.js";
|
||||
|
||||
export const createRoutineSchema = z.object({
|
||||
projectId: z.string().uuid(),
|
||||
goalId: z.string().uuid().optional().nullable(),
|
||||
parentIssueId: z.string().uuid().optional().nullable(),
|
||||
title: z.string().trim().min(1).max(200),
|
||||
description: z.string().optional().nullable(),
|
||||
assigneeAgentId: z.string().uuid(),
|
||||
priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"),
|
||||
status: z.enum(ROUTINE_STATUSES).optional().default("active"),
|
||||
concurrencyPolicy: z.enum(ROUTINE_CONCURRENCY_POLICIES).optional().default("coalesce_if_active"),
|
||||
catchUpPolicy: z.enum(ROUTINE_CATCH_UP_POLICIES).optional().default("skip_missed"),
|
||||
});
|
||||
|
||||
export type CreateRoutine = z.infer<typeof createRoutineSchema>;
|
||||
|
||||
export const updateRoutineSchema = createRoutineSchema.partial();
|
||||
export type UpdateRoutine = z.infer<typeof updateRoutineSchema>;
|
||||
|
||||
const baseTriggerSchema = z.object({
|
||||
label: z.string().trim().max(120).optional().nullable(),
|
||||
enabled: z.boolean().optional().default(true),
|
||||
});
|
||||
|
||||
export const createRoutineTriggerSchema = z.discriminatedUnion("kind", [
|
||||
baseTriggerSchema.extend({
|
||||
kind: z.literal("schedule"),
|
||||
cronExpression: z.string().trim().min(1),
|
||||
timezone: z.string().trim().min(1).default("UTC"),
|
||||
}),
|
||||
baseTriggerSchema.extend({
|
||||
kind: z.literal("webhook"),
|
||||
signingMode: z.enum(ROUTINE_TRIGGER_SIGNING_MODES).optional().default("bearer"),
|
||||
replayWindowSec: z.number().int().min(30).max(86_400).optional().default(300),
|
||||
}),
|
||||
baseTriggerSchema.extend({
|
||||
kind: z.literal("api"),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type CreateRoutineTrigger = z.infer<typeof createRoutineTriggerSchema>;
|
||||
|
||||
export const updateRoutineTriggerSchema = z.object({
|
||||
label: z.string().trim().max(120).optional().nullable(),
|
||||
enabled: z.boolean().optional(),
|
||||
cronExpression: z.string().trim().min(1).optional().nullable(),
|
||||
timezone: z.string().trim().min(1).optional().nullable(),
|
||||
signingMode: z.enum(ROUTINE_TRIGGER_SIGNING_MODES).optional().nullable(),
|
||||
replayWindowSec: z.number().int().min(30).max(86_400).optional().nullable(),
|
||||
});
|
||||
|
||||
export type UpdateRoutineTrigger = z.infer<typeof updateRoutineTriggerSchema>;
|
||||
|
||||
export const runRoutineSchema = z.object({
|
||||
triggerId: z.string().uuid().optional().nullable(),
|
||||
payload: z.record(z.unknown()).optional().nullable(),
|
||||
idempotencyKey: z.string().trim().max(255).optional().nullable(),
|
||||
source: z.enum(["manual", "api"]).optional().default("manual"),
|
||||
});
|
||||
|
||||
export type RunRoutine = z.infer<typeof runRoutineSchema>;
|
||||
|
||||
export const rotateRoutineTriggerSecretSchema = z.object({});
|
||||
export type RotateRoutineTriggerSecret = z.infer<typeof rotateRoutineTriggerSecretSchema>;
|
||||
@@ -0,0 +1,47 @@
|
||||
import { z } from "zod";
|
||||
import { SECRET_PROVIDERS } from "../constants.js";
|
||||
|
||||
export const envBindingPlainSchema = z.object({
|
||||
type: z.literal("plain"),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
export const envBindingSecretRefSchema = z.object({
|
||||
type: z.literal("secret_ref"),
|
||||
secretId: z.string().uuid(),
|
||||
version: z.union([z.literal("latest"), z.number().int().positive()]).optional(),
|
||||
});
|
||||
|
||||
// Backward-compatible union that accepts legacy inline values.
|
||||
export const envBindingSchema = z.union([
|
||||
z.string(),
|
||||
envBindingPlainSchema,
|
||||
envBindingSecretRefSchema,
|
||||
]);
|
||||
|
||||
export const envConfigSchema = z.record(envBindingSchema);
|
||||
|
||||
export const createSecretSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
provider: z.enum(SECRET_PROVIDERS).optional(),
|
||||
value: z.string().min(1),
|
||||
description: z.string().optional().nullable(),
|
||||
externalRef: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateSecret = z.infer<typeof createSecretSchema>;
|
||||
|
||||
export const rotateSecretSchema = z.object({
|
||||
value: z.string().min(1),
|
||||
externalRef: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type RotateSecret = z.infer<typeof rotateSecretSchema>;
|
||||
|
||||
export const updateSecretSchema = z.object({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().optional().nullable(),
|
||||
externalRef: z.string().optional().nullable(),
|
||||
});
|
||||
|
||||
export type UpdateSecret = z.infer<typeof updateSecretSchema>;
|
||||
@@ -0,0 +1,54 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const issueWorkProductTypeSchema = z.enum([
|
||||
"preview_url",
|
||||
"runtime_service",
|
||||
"pull_request",
|
||||
"branch",
|
||||
"commit",
|
||||
"artifact",
|
||||
"document",
|
||||
]);
|
||||
|
||||
export const issueWorkProductStatusSchema = z.enum([
|
||||
"active",
|
||||
"ready_for_review",
|
||||
"approved",
|
||||
"changes_requested",
|
||||
"merged",
|
||||
"closed",
|
||||
"failed",
|
||||
"archived",
|
||||
"draft",
|
||||
]);
|
||||
|
||||
export const issueWorkProductReviewStateSchema = z.enum([
|
||||
"none",
|
||||
"needs_board_review",
|
||||
"approved",
|
||||
"changes_requested",
|
||||
]);
|
||||
|
||||
export const createIssueWorkProductSchema = z.object({
|
||||
projectId: z.string().uuid().optional().nullable(),
|
||||
executionWorkspaceId: z.string().uuid().optional().nullable(),
|
||||
runtimeServiceId: z.string().uuid().optional().nullable(),
|
||||
type: issueWorkProductTypeSchema,
|
||||
provider: z.string().min(1),
|
||||
externalId: z.string().optional().nullable(),
|
||||
title: z.string().min(1),
|
||||
url: z.string().url().optional().nullable(),
|
||||
status: issueWorkProductStatusSchema.default("active"),
|
||||
reviewState: issueWorkProductReviewStateSchema.optional().default("none"),
|
||||
isPrimary: z.boolean().optional().default(false),
|
||||
healthStatus: z.enum(["unknown", "healthy", "unhealthy"]).optional().default("unknown"),
|
||||
summary: z.string().optional().nullable(),
|
||||
metadata: z.record(z.unknown()).optional().nullable(),
|
||||
createdByRunId: z.string().uuid().optional().nullable(),
|
||||
});
|
||||
|
||||
export type CreateIssueWorkProduct = z.infer<typeof createIssueWorkProductSchema>;
|
||||
|
||||
export const updateIssueWorkProductSchema = createIssueWorkProductSchema.partial();
|
||||
|
||||
export type UpdateIssueWorkProduct = z.infer<typeof updateIssueWorkProductSchema>;
|
||||
Reference in new issue
Block a user