[codex] Add teams catalog extraction (#7550)
Fixes #7551 ## Thinking Path > - Paperclip is the control plane for AI-agent companies, and reusable company/team setup is part of making those companies faster to launch. > - The teams catalog work introduces app-shipped team templates that can be browsed, previewed, and installed into a company. > - Catalog installation crosses several contracts: bundled package contents, shared API types, server import/install behavior, CLI workflows, and the board UI. > - Agents also need a safe path through catalog installs: scoped company selection, explicit source policy, approval fallback for agent creation, and preserved catalog provenance. > - This pull request extracts the completed teams catalog branch into one reviewable PR on top of `public-gh/master`. > - The benefit is a reusable teams catalog foundation with server, CLI, package, docs, and hidden UI surfaces kept in sync. ## What Changed - Added the `@paperclipai/teams-catalog` package with bundled/optional team definitions, generated manifest, validators, catalog builder tests, and migration notes. - Added shared teams catalog types/validators plus server routes and services for listing, previewing, and installing catalog teams. - Integrated catalog install with company portability, skill/source policy checks, provenance metadata, origin hashes, target-manager reparenting, and installed/out-of-date detection. - Added CLI `teams` commands and agent-safe company selection behavior, including `company current` and approval fallback for forbidden agent-run installs. - Added hidden Team Catalog UI/API/query surfaces, Storybook fixtures, and targeted UI tests while keeping the UI route out of primary navigation. - Added docs for CLI/company/teams catalog behavior and removed generated screenshot artifacts from the PR diff. ## Verification - `pnpm exec vitest run cli/src/__tests__/company.test.ts cli/src/__tests__/teams.test.ts packages/teams-catalog/src/catalog-builder.test.ts packages/teams-catalog/src/shipped-catalog.test.ts server/src/__tests__/agent-permissions-service.test.ts server/src/__tests__/company-portability.test.ts server/src/__tests__/company-skills-service.test.ts server/src/__tests__/teams-catalog-routes.test.ts server/src/__tests__/teams-catalog-service.test.ts server/src/__tests__/teams-catalog-install-no-overrides.test.ts ui/src/lib/company-routes.test.ts ui/src/pages/TeamCard.test.tsx ui/src/pages/TeamCatalog.test.tsx ui/src/pages/useInstallTeamCatalogEntry.test.tsx` - `pnpm --filter @paperclipai/shared typecheck && pnpm --filter @paperclipai/teams-catalog typecheck && pnpm --filter paperclipai typecheck && pnpm --filter @paperclipai/server typecheck && pnpm --filter @paperclipai/ui typecheck` - Confirmed branch is rebased onto `public-gh/master` (`78dc3625a`) and `public-gh/master` is an ancestor of `HEAD`. - Confirmed PR diff excludes `pnpm-lock.yaml`, `.github/workflows/*`, generated screenshot images, and screenshot helper scripts. ## Risks - Medium review surface: this crosses package generation, shared contracts, server install behavior, CLI, docs, and hidden UI code. - Catalog install behavior creates agents/projects/tasks/skills and must keep company scoping, permissions, source policy, and provenance checks strict. - `pnpm-lock.yaml` is intentionally excluded per repo policy; CI/default-branch automation owns lockfile refresh. - The Team Catalog UI is included but hidden from primary navigation, so future enablement should re-check visual QA before exposure. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. > > ROADMAP checked: this aligns with reusable companies/templates and plugin-adjacent onboarding work. This PR packages work already developed on the Paperclip task branch for review. ## Model Used - OpenAI Codex, GPT-5 series coding agent in this Paperclip session; exact runtime context window was not exposed. Used shell, git, `gh`, and local test/typecheck tooling. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] If this change affects the UI, I have included before/after screenshots, or documented why screenshots are intentionally omitted - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
export interface MarkdownDoc {
|
||||
frontmatter: Record<string, unknown>;
|
||||
body: string;
|
||||
hasFrontmatter: boolean;
|
||||
}
|
||||
|
||||
export function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function asString(value: unknown): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : null;
|
||||
}
|
||||
|
||||
export function asBoolean(value: unknown): boolean | null {
|
||||
return typeof value === "boolean" ? value : null;
|
||||
}
|
||||
|
||||
export function asStringArray(value: unknown): string[] | null {
|
||||
if (value === undefined) return [];
|
||||
if (!Array.isArray(value)) return null;
|
||||
|
||||
const out: string[] = [];
|
||||
for (const item of value) {
|
||||
const text = asString(item);
|
||||
if (!text) return null;
|
||||
out.push(text);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function parseFrontmatterMarkdown(raw: string): MarkdownDoc {
|
||||
const normalized = raw.replace(/\r\n/g, "\n");
|
||||
if (!normalized.startsWith("---\n")) {
|
||||
return { frontmatter: {}, body: normalized.trim(), hasFrontmatter: false };
|
||||
}
|
||||
|
||||
const closing = normalized.indexOf("\n---\n", 4);
|
||||
if (closing < 0) {
|
||||
return { frontmatter: {}, body: normalized.trim(), hasFrontmatter: false };
|
||||
}
|
||||
|
||||
const frontmatterRaw = normalized.slice(4, closing).trim();
|
||||
const body = normalized.slice(closing + 5).trim();
|
||||
return {
|
||||
frontmatter: parseYamlFrontmatter(frontmatterRaw),
|
||||
body,
|
||||
hasFrontmatter: true,
|
||||
};
|
||||
}
|
||||
|
||||
function parseYamlFrontmatter(raw: string): Record<string, unknown> {
|
||||
const prepared = prepareYamlLines(raw);
|
||||
if (prepared.length === 0) return {};
|
||||
const parsed = parseYamlBlock(prepared, 0, prepared[0]!.indent);
|
||||
return isPlainRecord(parsed.value) ? parsed.value : {};
|
||||
}
|
||||
|
||||
function prepareYamlLines(raw: string) {
|
||||
return raw
|
||||
.split("\n")
|
||||
.map((line) => ({
|
||||
indent: line.match(/^ */)?.[0].length ?? 0,
|
||||
content: line.trim(),
|
||||
}))
|
||||
.filter((line) => line.content.length > 0 && !line.content.startsWith("#"));
|
||||
}
|
||||
|
||||
function parseYamlBlock(
|
||||
lines: Array<{ indent: number; content: string }>,
|
||||
startIndex: number,
|
||||
indentLevel: number,
|
||||
): { value: unknown; nextIndex: number } {
|
||||
let index = startIndex;
|
||||
if (index >= lines.length || lines[index]!.indent < indentLevel) {
|
||||
return { value: {}, nextIndex: index };
|
||||
}
|
||||
|
||||
const isArray = lines[index]!.indent === indentLevel && lines[index]!.content.startsWith("-");
|
||||
if (isArray) {
|
||||
const values: unknown[] = [];
|
||||
while (index < lines.length) {
|
||||
const line = lines[index]!;
|
||||
if (line.indent < indentLevel) break;
|
||||
if (line.indent !== indentLevel || !line.content.startsWith("-")) break;
|
||||
|
||||
const remainder = line.content.slice(1).trim();
|
||||
index += 1;
|
||||
if (!remainder) {
|
||||
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
||||
values.push(nested.value);
|
||||
index = nested.nextIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
values.push(parseYamlScalar(remainder));
|
||||
}
|
||||
return { value: values, nextIndex: index };
|
||||
}
|
||||
|
||||
const record: Record<string, unknown> = {};
|
||||
while (index < lines.length) {
|
||||
const line = lines[index]!;
|
||||
if (line.indent < indentLevel) break;
|
||||
if (line.indent !== indentLevel) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const separatorIndex = line.content.indexOf(":");
|
||||
if (separatorIndex <= 0) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = line.content.slice(0, separatorIndex).trim();
|
||||
const remainder = line.content.slice(separatorIndex + 1).trim();
|
||||
index += 1;
|
||||
if (!remainder) {
|
||||
const nested = parseYamlBlock(lines, index, indentLevel + 2);
|
||||
record[key] = nested.value;
|
||||
index = nested.nextIndex;
|
||||
continue;
|
||||
}
|
||||
record[key] = parseYamlScalar(remainder);
|
||||
}
|
||||
|
||||
return { value: record, nextIndex: index };
|
||||
}
|
||||
|
||||
function parseYamlScalar(rawValue: string): unknown {
|
||||
const trimmed = rawValue.trim();
|
||||
if (trimmed === "") return "";
|
||||
if (trimmed === "null" || trimmed === "~") return null;
|
||||
if (trimmed === "true") return true;
|
||||
if (trimmed === "false") return false;
|
||||
if (trimmed === "[]") return [];
|
||||
if (trimmed === "{}") return {};
|
||||
if (/^-?\d+(\.\d)?\d*$/.test(trimmed)) return Number(trimmed);
|
||||
if (
|
||||
trimmed.startsWith("\"") ||
|
||||
trimmed.startsWith("[") ||
|
||||
trimmed.startsWith("{")
|
||||
) {
|
||||
try {
|
||||
return JSON.parse(trimmed);
|
||||
} catch {
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
export { agentAdapterTypeSchema, optionalAgentAdapterTypeSchema } from "./adapter-type.js";
|
||||
export {
|
||||
asBoolean,
|
||||
asString,
|
||||
asStringArray,
|
||||
isPlainRecord as isFrontmatterPlainRecord,
|
||||
parseFrontmatterMarkdown,
|
||||
type MarkdownDoc,
|
||||
} from "./frontmatter.js";
|
||||
export {
|
||||
COMPANY_STATUSES,
|
||||
DEFAULT_COMPANY_ATTACHMENT_MAX_BYTES,
|
||||
@@ -322,6 +330,27 @@ export type {
|
||||
CatalogSkillFileDetail,
|
||||
CompanySkillInstallCatalogRequest,
|
||||
CompanySkillInstallCatalogResult,
|
||||
CatalogTeamKind,
|
||||
CatalogTeamTrustLevel,
|
||||
CatalogTeamCompatibility,
|
||||
CatalogTeamFileKind,
|
||||
CatalogTeamSkillRequirementType,
|
||||
CatalogTeamSkillRequirement,
|
||||
CatalogTeamEnvInputSummary,
|
||||
CatalogTeamSourceRef,
|
||||
CatalogTeamFile,
|
||||
CatalogTeam,
|
||||
CatalogManifest,
|
||||
CatalogTeamListQuery,
|
||||
CatalogTeamFileDetail,
|
||||
CatalogTeamSourcePolicy,
|
||||
CatalogTeamImportOptions,
|
||||
CatalogTeamInstallOptions,
|
||||
CatalogTeamSkillPreparationAction,
|
||||
CatalogTeamSkillPreparation,
|
||||
CatalogTeamImportPreviewResult,
|
||||
CatalogTeamInstallResult,
|
||||
InstalledCatalogTeam,
|
||||
AgentSkillSyncMode,
|
||||
AgentSkillState,
|
||||
AgentSkillOrigin,
|
||||
@@ -1110,6 +1139,22 @@ export {
|
||||
companySkillInstallCatalogResultSchema,
|
||||
companySkillInstallUpdateSchema,
|
||||
companySkillResetSchema,
|
||||
catalogTeamKindSchema,
|
||||
catalogTeamTrustLevelSchema,
|
||||
catalogTeamCompatibilitySchema,
|
||||
catalogTeamFileKindSchema,
|
||||
catalogTeamSkillRequirementTypeSchema,
|
||||
catalogTeamSkillRequirementSchema,
|
||||
catalogTeamEnvInputSummarySchema,
|
||||
catalogTeamSourceRefSchema,
|
||||
catalogTeamFileSchema,
|
||||
catalogTeamSchema,
|
||||
catalogTeamListQuerySchema,
|
||||
catalogTeamFileDetailSchema,
|
||||
catalogTeamSourcePolicySchema,
|
||||
catalogTeamPreviewSchema,
|
||||
catalogTeamInstallSchema,
|
||||
catalogTeamSkillPreparationSchema,
|
||||
portabilityIncludeSchema,
|
||||
portabilityEnvInputSchema,
|
||||
portabilityCompanyManifestEntrySchema,
|
||||
|
||||
@@ -138,6 +138,8 @@ export interface CompanyPortabilityAgentManifestEntry {
|
||||
icon: string | null;
|
||||
capabilities: string | null;
|
||||
reportsToSlug: string | null;
|
||||
reportsToExistingAgentId: string | null;
|
||||
reportsToExistingAgentSlug: string | null;
|
||||
adapterType: string;
|
||||
adapterConfig: Record<string, unknown>;
|
||||
runtimeConfig: Record<string, unknown>;
|
||||
@@ -294,6 +296,7 @@ export interface CompanyPortabilityAdapterOverride {
|
||||
|
||||
export interface CompanyPortabilityImportRequest extends CompanyPortabilityPreviewRequest {
|
||||
adapterOverrides?: Record<string, CompanyPortabilityAdapterOverride>;
|
||||
secretValues?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface CompanyPortabilityImportResult {
|
||||
|
||||
@@ -78,6 +78,29 @@ export type {
|
||||
CompanySkillInstallCatalogRequest,
|
||||
CompanySkillInstallCatalogResult,
|
||||
} from "./company-skill.js";
|
||||
export type {
|
||||
CatalogTeamKind,
|
||||
CatalogTeamTrustLevel,
|
||||
CatalogTeamCompatibility,
|
||||
CatalogTeamFileKind,
|
||||
CatalogTeamSkillRequirementType,
|
||||
CatalogTeamSkillRequirement,
|
||||
CatalogTeamEnvInputSummary,
|
||||
CatalogTeamSourceRef,
|
||||
CatalogTeamFile,
|
||||
CatalogTeam,
|
||||
CatalogManifest,
|
||||
CatalogTeamListQuery,
|
||||
CatalogTeamFileDetail,
|
||||
CatalogTeamSourcePolicy,
|
||||
CatalogTeamImportOptions,
|
||||
CatalogTeamInstallOptions,
|
||||
CatalogTeamSkillPreparationAction,
|
||||
CatalogTeamSkillPreparation,
|
||||
CatalogTeamImportPreviewResult,
|
||||
CatalogTeamInstallResult,
|
||||
InstalledCatalogTeam,
|
||||
} from "./teams-catalog.js";
|
||||
export type {
|
||||
AgentSkillSyncMode,
|
||||
AgentSkillState,
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
import type {
|
||||
CompanyPortabilityAdapterOverride,
|
||||
CompanyPortabilityAgentSelection,
|
||||
CompanyPortabilityCollisionStrategy,
|
||||
CompanyPortabilityImportResult,
|
||||
CompanyPortabilityInclude,
|
||||
CompanyPortabilityPreviewResult,
|
||||
} from "./company-portability.js";
|
||||
|
||||
export type CatalogTeamKind = "bundled" | "optional";
|
||||
|
||||
export type CatalogTeamTrustLevel =
|
||||
| "markdown_only"
|
||||
| "assets"
|
||||
| "scripts_executables"
|
||||
| "external_sources";
|
||||
|
||||
export type CatalogTeamCompatibility = "compatible" | "unknown" | "invalid";
|
||||
|
||||
export type CatalogTeamFileKind =
|
||||
| "team"
|
||||
| "agent"
|
||||
| "project"
|
||||
| "task"
|
||||
| "skill"
|
||||
| "extension"
|
||||
| "readme"
|
||||
| "reference"
|
||||
| "script"
|
||||
| "asset"
|
||||
| "markdown"
|
||||
| "other";
|
||||
|
||||
export type CatalogTeamSkillRequirementType =
|
||||
| "catalog"
|
||||
| "local"
|
||||
| "skills_sh"
|
||||
| "github"
|
||||
| "url"
|
||||
| "local_path"
|
||||
| "agent_package";
|
||||
|
||||
export interface CatalogTeamSkillRequirement {
|
||||
type: CatalogTeamSkillRequirementType;
|
||||
ref: string;
|
||||
agentSlugs: string[];
|
||||
resolved: boolean;
|
||||
catalogSkillId?: string;
|
||||
catalogSkillKey?: string;
|
||||
localPath?: string;
|
||||
sourceLocator?: string;
|
||||
sourceRef?: string;
|
||||
}
|
||||
|
||||
export interface CatalogTeamEnvInputSummary {
|
||||
key: string;
|
||||
agentSlug: string | null;
|
||||
projectSlug: string | null;
|
||||
kind: "secret" | "plain";
|
||||
requirement: "required" | "optional";
|
||||
}
|
||||
|
||||
export interface CatalogTeamSourceRef {
|
||||
type: Exclude<CatalogTeamSkillRequirementType, "catalog" | "local"> | "include";
|
||||
ref: string;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
export interface CatalogTeamFile {
|
||||
path: string;
|
||||
kind: CatalogTeamFileKind;
|
||||
sizeBytes: number;
|
||||
sha256: string;
|
||||
}
|
||||
|
||||
export interface CatalogTeam {
|
||||
id: string;
|
||||
key: string;
|
||||
kind: CatalogTeamKind;
|
||||
category: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string;
|
||||
path: string;
|
||||
entrypoint: "TEAM.md";
|
||||
schema: "agentcompanies/v1";
|
||||
defaultInstall: boolean;
|
||||
recommendedForCompanyTypes: string[];
|
||||
tags: string[];
|
||||
counts: {
|
||||
agents: number;
|
||||
projects: number;
|
||||
tasks: number;
|
||||
routines: number;
|
||||
localSkills: number;
|
||||
catalogSkills: number;
|
||||
externalSkillSources: number;
|
||||
};
|
||||
rootAgentSlugs: string[];
|
||||
agentSlugs: string[];
|
||||
projectSlugs: string[];
|
||||
requiredSkills: CatalogTeamSkillRequirement[];
|
||||
envInputs: CatalogTeamEnvInputSummary[];
|
||||
sourceRefs: CatalogTeamSourceRef[];
|
||||
files: CatalogTeamFile[];
|
||||
trustLevel: CatalogTeamTrustLevel;
|
||||
compatibility: CatalogTeamCompatibility;
|
||||
contentHash: string;
|
||||
packageName?: string;
|
||||
packageVersion?: string;
|
||||
}
|
||||
|
||||
export interface CatalogManifest {
|
||||
schemaVersion: 1;
|
||||
packageName: "@paperclipai/teams-catalog";
|
||||
packageVersion: string;
|
||||
generatedAt: string;
|
||||
teams: CatalogTeam[];
|
||||
}
|
||||
|
||||
export interface CatalogTeamListQuery {
|
||||
kind?: CatalogTeamKind;
|
||||
category?: string;
|
||||
q?: string;
|
||||
}
|
||||
|
||||
export interface CatalogTeamFileDetail {
|
||||
catalogTeamId: string;
|
||||
path: string;
|
||||
kind: CatalogTeamFileKind;
|
||||
content: string;
|
||||
language: string | null;
|
||||
markdown: boolean;
|
||||
}
|
||||
|
||||
export interface CatalogTeamSourcePolicy {
|
||||
allowExternalSources?: boolean;
|
||||
allowUnpinnedOptionalSources?: boolean;
|
||||
allowLocalPathSources?: boolean;
|
||||
}
|
||||
|
||||
export interface CatalogTeamImportOptions {
|
||||
targetManagerAgentId?: string | null;
|
||||
targetManagerSlug?: string | null;
|
||||
include?: Partial<CompanyPortabilityInclude>;
|
||||
agents?: CompanyPortabilityAgentSelection;
|
||||
collisionStrategy?: CompanyPortabilityCollisionStrategy;
|
||||
nameOverrides?: Record<string, string>;
|
||||
selectedFiles?: string[];
|
||||
sourcePolicy?: CatalogTeamSourcePolicy;
|
||||
}
|
||||
|
||||
export interface CatalogTeamInstallOptions extends CatalogTeamImportOptions {
|
||||
adapterOverrides?: Record<string, CompanyPortabilityAdapterOverride>;
|
||||
secretValues?: Record<string, string>;
|
||||
}
|
||||
|
||||
export type CatalogTeamSkillPreparationAction =
|
||||
| "already_in_package"
|
||||
| "catalog_install_required"
|
||||
| "external_import_required"
|
||||
| "blocked";
|
||||
|
||||
export interface CatalogTeamSkillPreparation {
|
||||
type: CatalogTeamSkillRequirementType;
|
||||
ref: string;
|
||||
agentSlugs: string[];
|
||||
action: CatalogTeamSkillPreparationAction;
|
||||
catalogSkillId: string | null;
|
||||
catalogSkillKey: string | null;
|
||||
sourceLocator: string | null;
|
||||
sourceRef: string | null;
|
||||
reason: string | null;
|
||||
}
|
||||
|
||||
export interface CatalogTeamImportPreviewResult {
|
||||
team: CatalogTeam;
|
||||
portabilityPreview: CompanyPortabilityPreviewResult;
|
||||
skillPreparations: CatalogTeamSkillPreparation[];
|
||||
warnings: string[];
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface CatalogTeamInstallResult {
|
||||
team: CatalogTeam;
|
||||
portabilityImport: CompanyPortabilityImportResult;
|
||||
skillPreparations: CatalogTeamSkillPreparation[];
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-computed installed-team state for a company. Surfaced by
|
||||
* `GET /api/companies/:companyId/teams/catalog/installed` and consumed by the
|
||||
* Team Catalog UI to render the `INSTALLED · N` group, per-row out-of-date
|
||||
* badges, and the detail header "Update available" chip (design
|
||||
* [PAP-10238 §3.2 + §5]).
|
||||
*
|
||||
* `outOfDate` is true when at least one installed agent carries a
|
||||
* `metadata.paperclip.catalogTeam.originHash` that differs from the catalog
|
||||
* team's current `contentHash`. `present` is false when the installed team no
|
||||
* longer resolves to a catalog entry (e.g. removed from the package) — in that
|
||||
* case the comparison is unknown and `outOfDate` stays false.
|
||||
*/
|
||||
export interface InstalledCatalogTeam {
|
||||
catalogId: string;
|
||||
catalogKey: string | null;
|
||||
/** True when the installed catalogId still resolves to a current catalog team. */
|
||||
present: boolean;
|
||||
/** Current catalog `contentHash` for this team, or null when not present. */
|
||||
currentContentHash: string | null;
|
||||
/** Distinct `originHash` values recorded across installed agents. */
|
||||
installedOriginHashes: string[];
|
||||
/** Number of non-terminated agents carrying this team's provenance. */
|
||||
agentCount: number;
|
||||
/** True when a present team has at least one stale installed originHash. */
|
||||
outOfDate: boolean;
|
||||
}
|
||||
@@ -256,6 +256,7 @@ export const portabilityAdapterOverrideSchema = z.object({
|
||||
|
||||
export const companyPortabilityImportSchema = companyPortabilityPreviewSchema.extend({
|
||||
adapterOverrides: z.record(z.string().min(1), portabilityAdapterOverrideSchema).optional(),
|
||||
secretValues: z.record(z.string().min(1), z.string()).optional(),
|
||||
});
|
||||
|
||||
export type CompanyPortabilityImport = z.infer<typeof companyPortabilityImportSchema>;
|
||||
|
||||
@@ -97,6 +97,27 @@ export {
|
||||
type CompanySkillInstallUpdate,
|
||||
type CompanySkillReset,
|
||||
} from "./company-skill.js";
|
||||
export {
|
||||
catalogTeamKindSchema,
|
||||
catalogTeamTrustLevelSchema,
|
||||
catalogTeamCompatibilitySchema,
|
||||
catalogTeamFileKindSchema,
|
||||
catalogTeamSkillRequirementTypeSchema,
|
||||
catalogTeamSkillRequirementSchema,
|
||||
catalogTeamEnvInputSummarySchema,
|
||||
catalogTeamSourceRefSchema,
|
||||
catalogTeamFileSchema,
|
||||
catalogTeamSchema,
|
||||
catalogTeamListQuerySchema,
|
||||
catalogTeamFileDetailSchema,
|
||||
catalogTeamSourcePolicySchema,
|
||||
catalogTeamPreviewSchema,
|
||||
catalogTeamInstallSchema,
|
||||
catalogTeamSkillPreparationSchema,
|
||||
type CatalogTeamListQuery,
|
||||
type CatalogTeamPreview,
|
||||
type CatalogTeamInstall,
|
||||
} from "./teams-catalog.js";
|
||||
export {
|
||||
agentSkillStateSchema,
|
||||
agentSkillSyncModeSchema,
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
portabilityAdapterOverrideSchema,
|
||||
portabilityAgentSelectionSchema,
|
||||
portabilityCollisionStrategySchema,
|
||||
portabilityIncludeSchema,
|
||||
} from "./company-portability.js";
|
||||
|
||||
export const catalogTeamKindSchema = z.enum(["bundled", "optional"]);
|
||||
|
||||
export const catalogTeamTrustLevelSchema = z.enum([
|
||||
"markdown_only",
|
||||
"assets",
|
||||
"scripts_executables",
|
||||
"external_sources",
|
||||
]);
|
||||
|
||||
export const catalogTeamCompatibilitySchema = z.enum(["compatible", "unknown", "invalid"]);
|
||||
|
||||
export const catalogTeamFileKindSchema = z.enum([
|
||||
"team",
|
||||
"agent",
|
||||
"project",
|
||||
"task",
|
||||
"skill",
|
||||
"extension",
|
||||
"readme",
|
||||
"reference",
|
||||
"script",
|
||||
"asset",
|
||||
"markdown",
|
||||
"other",
|
||||
]);
|
||||
|
||||
export const catalogTeamSkillRequirementTypeSchema = z.enum([
|
||||
"catalog",
|
||||
"local",
|
||||
"skills_sh",
|
||||
"github",
|
||||
"url",
|
||||
"local_path",
|
||||
"agent_package",
|
||||
]);
|
||||
|
||||
export const catalogTeamSkillRequirementSchema = z.object({
|
||||
type: catalogTeamSkillRequirementTypeSchema,
|
||||
ref: z.string().min(1),
|
||||
agentSlugs: z.array(z.string().min(1)),
|
||||
resolved: z.boolean(),
|
||||
catalogSkillId: z.string().min(1).optional(),
|
||||
catalogSkillKey: z.string().min(1).optional(),
|
||||
localPath: z.string().min(1).optional(),
|
||||
sourceLocator: z.string().min(1).optional(),
|
||||
sourceRef: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
export const catalogTeamEnvInputSummarySchema = z.object({
|
||||
key: z.string().min(1),
|
||||
agentSlug: z.string().min(1).nullable(),
|
||||
projectSlug: z.string().min(1).nullable(),
|
||||
kind: z.enum(["secret", "plain"]),
|
||||
requirement: z.enum(["required", "optional"]),
|
||||
});
|
||||
|
||||
export const catalogTeamSourceRefSchema = z.object({
|
||||
type: z.enum(["skills_sh", "github", "url", "local_path", "agent_package", "include"]),
|
||||
ref: z.string().min(1),
|
||||
pinned: z.boolean(),
|
||||
});
|
||||
|
||||
export const catalogTeamFileSchema = z.object({
|
||||
path: z.string().min(1),
|
||||
kind: catalogTeamFileKindSchema,
|
||||
sizeBytes: z.number().int().nonnegative(),
|
||||
sha256: z.string().min(1),
|
||||
});
|
||||
|
||||
export const catalogTeamSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
key: z.string().min(1),
|
||||
kind: catalogTeamKindSchema,
|
||||
category: z.string().min(1),
|
||||
slug: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
description: z.string(),
|
||||
path: z.string().min(1),
|
||||
entrypoint: z.literal("TEAM.md"),
|
||||
schema: z.literal("agentcompanies/v1"),
|
||||
defaultInstall: z.boolean(),
|
||||
recommendedForCompanyTypes: z.array(z.string()),
|
||||
tags: z.array(z.string()),
|
||||
counts: z.object({
|
||||
agents: z.number().int().nonnegative(),
|
||||
projects: z.number().int().nonnegative(),
|
||||
tasks: z.number().int().nonnegative(),
|
||||
routines: z.number().int().nonnegative(),
|
||||
localSkills: z.number().int().nonnegative(),
|
||||
catalogSkills: z.number().int().nonnegative(),
|
||||
externalSkillSources: z.number().int().nonnegative(),
|
||||
}),
|
||||
rootAgentSlugs: z.array(z.string()),
|
||||
agentSlugs: z.array(z.string()),
|
||||
projectSlugs: z.array(z.string()),
|
||||
requiredSkills: z.array(catalogTeamSkillRequirementSchema),
|
||||
envInputs: z.array(catalogTeamEnvInputSummarySchema),
|
||||
sourceRefs: z.array(catalogTeamSourceRefSchema),
|
||||
files: z.array(catalogTeamFileSchema),
|
||||
trustLevel: catalogTeamTrustLevelSchema,
|
||||
compatibility: catalogTeamCompatibilitySchema,
|
||||
contentHash: z.string().min(1),
|
||||
packageName: z.string().min(1).optional(),
|
||||
packageVersion: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
export const catalogTeamListQuerySchema = z.object({
|
||||
kind: catalogTeamKindSchema.optional(),
|
||||
category: z.string().min(1).optional(),
|
||||
q: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
export const catalogTeamFileDetailSchema = z.object({
|
||||
catalogTeamId: z.string().min(1),
|
||||
path: z.string().min(1),
|
||||
kind: catalogTeamFileKindSchema,
|
||||
content: z.string(),
|
||||
language: z.string().nullable(),
|
||||
markdown: z.boolean(),
|
||||
});
|
||||
|
||||
export const catalogTeamSourcePolicySchema = z.object({
|
||||
allowExternalSources: z.boolean().optional(),
|
||||
allowUnpinnedOptionalSources: z.boolean().optional(),
|
||||
allowLocalPathSources: z.boolean().optional(),
|
||||
}).strict();
|
||||
|
||||
export const catalogTeamPreviewSchema = z.object({
|
||||
targetManagerAgentId: z.string().min(1).nullable().optional(),
|
||||
targetManagerSlug: z.string().min(1).nullable().optional(),
|
||||
include: portabilityIncludeSchema.omit({ company: true }).strict().optional(),
|
||||
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(),
|
||||
sourcePolicy: catalogTeamSourcePolicySchema.optional(),
|
||||
}).strict();
|
||||
|
||||
export const catalogTeamInstallSchema = catalogTeamPreviewSchema.extend({
|
||||
adapterOverrides: z.record(z.string().min(1), portabilityAdapterOverrideSchema).optional(),
|
||||
secretValues: z.record(z.string().min(1), z.string()).optional(),
|
||||
}).strict();
|
||||
|
||||
export const catalogTeamSkillPreparationSchema = z.object({
|
||||
type: catalogTeamSkillRequirementTypeSchema,
|
||||
ref: z.string().min(1),
|
||||
agentSlugs: z.array(z.string().min(1)),
|
||||
action: z.enum([
|
||||
"already_in_package",
|
||||
"catalog_install_required",
|
||||
"external_import_required",
|
||||
"blocked",
|
||||
]),
|
||||
catalogSkillId: z.string().min(1).nullable(),
|
||||
catalogSkillKey: z.string().min(1).nullable(),
|
||||
sourceLocator: z.string().min(1).nullable(),
|
||||
sourceRef: z.string().min(1).nullable(),
|
||||
reason: z.string().min(1).nullable(),
|
||||
});
|
||||
|
||||
export type CatalogTeamListQuery = z.infer<typeof catalogTeamListQuerySchema>;
|
||||
export type CatalogTeamPreview = z.infer<typeof catalogTeamPreviewSchema>;
|
||||
export type CatalogTeamInstall = z.infer<typeof catalogTeamInstallSchema>;
|
||||
Reference in New Issue
Block a user