{
  "$id": "https://glide.co/schemas/agent-banking/draft/skill-manifest.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "SkillManifest",
  "description": "The OSS agent-skill manifest. Every package at packages/skills/<id>/ ships one of these as src/manifest.ts. Validated by the manifest CI gate (Lane D) and at install time by the consent-flow router. `consentSummary` is surfaced verbatim at the consent step. Forward-compat: category, runtime, and scope vocabularies grow additively.",
  "channel": "v1",
  "examples": [
    {
      "schemaVersion": "v1",
      "slug": "ap-bill-pay",
      "displayName": "AP Bill Pay",
      "tagline": "Pay vetted vendors automatically.",
      "longDescription": "Initiates payments to your pre-approved vendor list, capped at $X/day, with optional human review for new payees.",
      "category": "ap",
      "runtimeCompat": ["claude-desktop", "chatgpt-apps"],
      "requiredScopes": ["payments:initiate", "accounts:read"],
      "policyTemplate": {
        "perTxMaxUsdCents": 50000,
        "dailyCapUsdCents": 200000,
        "velocityCap": { "windowMinutes": 60, "maxCount": 5 },
        "counterpartyAllowlist": [],
        "timeWindow": {
          "timezone": "America/New_York",
          "allowedHoursLocal": [9, 10, 11, 12, 13, 14, 15, 16, 17]
        }
      },
      "consentSummary": "This skill can initiate payments up to $500/day to your pre-approved vendors during business hours.",
      "trust": "core",
      "publisher": "Glide",
      "iconPath": "./icon.svg",
      "packageVersion": "0.2.0",
      "blockingDependency": null
    }
  ],
  "schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "schemaVersion": {
        "type": "string",
        "const": "v1",
        "description": "Schema major version. Pinned to 'v1'. Manifests with a different value are refused at install time."
      },
      "slug": {
        "$ref": "_types.json#/$defs/kebabSlug",
        "description": "URL-safe identifier. Surfaces in /skills/[slug] and at the npm package name `@glideco/skill-<slug>`. Globally unique across the skill registry."
      },
      "displayName": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64,
        "description": "Human-friendly name shown in the catalog."
      },
      "tagline": {
        "type": "string",
        "minLength": 1,
        "maxLength": 120,
        "description": "One-line pitch. Plain text, no markdown."
      },
      "longDescription": {
        "type": "string",
        "minLength": 1,
        "maxLength": 4096,
        "description": "Extended description for the catalog detail page. Plain text or simple markdown (paragraphs, lists). No HTML."
      },
      "category": {
        "$ref": "_types.json#/$defs/skillCategory",
        "description": "Catalog category. Mirrors hero-skills.ts."
      },
      "runtimeCompat": {
        "type": "array",
        "minItems": 1,
        "uniqueItems": true,
        "description": "Runtimes this skill supports. Skill code must run successfully under each runtime listed here; CI runs the contract suite per-runtime.",
        "items": { "$ref": "_types.json#/$defs/skillRuntime" }
      },
      "requiredScopes": {
        "type": "array",
        "minItems": 1,
        "uniqueItems": true,
        "description": "OAuth scopes the skill needs at install time. STRICT subset of agentScope — administrative scopes (`agent:budget:create`, `agent:budget:revoke`, `treasury:rotate-signer`) are intentionally not requestable from a skill manifest; they're only granted via in-app admin actions.",
        "items": { "$ref": "_types.json#/$defs/skillScope" }
      },
      "policyTemplate": {
        "type": "object",
        "description": "Per-skill default policy envelope template. The principal can tighten (but not loosen) at install time. Mirrors a strict subset of AgentPolicyEnvelope; skills only specify the axes that make sense for their flow.",
        "properties": {
          "perTxMaxUsdCents": {
            "$ref": "_types.json#/$defs/amountCents",
            "description": "Per-transaction max in USD cents. Renders as the default value in the install-time consent UI."
          },
          "dailyCapUsdCents": {
            "$ref": "_types.json#/$defs/amountCents",
            "description": "Rolling 24-hour window cap in USD cents."
          },
          "velocityCap": {
            "type": "object",
            "description": "Sliding-window transaction-count cap. Window is `windowMinutes` minutes wide; `maxCount` is the upper bound on tx count within any such window.",
            "properties": {
              "windowMinutes": {
                "$ref": "_types.json#/$defs/positiveInt",
                "description": "Window length in minutes. Strictly positive; up to 1440 (24h)."
              },
              "maxCount": {
                "$ref": "_types.json#/$defs/positiveInt",
                "description": "Max transactions in any rolling window of `windowMinutes` minutes."
              }
            },
            "required": ["windowMinutes", "maxCount"],
            "additionalProperties": false
          },
          "counterpartyAllowlist": {
            "type": "array",
            "uniqueItems": true,
            "description": "Pre-approved beneficiary IDs / wallet addresses. Empty = open allowlist (skill must populate at install time). Items are free-text identifiers (resolved by the skill's connector at runtime); strict-format validation belongs on AgentPolicyEnvelope.counterparty_allowlist.",
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            }
          },
          "timeWindow": {
            "type": "object",
            "description": "Recurring time-of-day window in a named IANA timezone. Distinct from AgentPolicyEnvelope.time_window_start/end (which are absolute instants).",
            "properties": {
              "timezone": {
                "$ref": "_types.json#/$defs/ianaTimezone",
                "description": "IANA tz database name."
              },
              "allowedHoursLocal": {
                "type": "array",
                "uniqueItems": true,
                "minItems": 1,
                "maxItems": 24,
                "description": "Hours-of-day (0-23) when the skill may transact. Local to `timezone`. Empty array is rejected (use the absence of timeWindow itself to mean 'any hour').",
                "items": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 23
                }
              }
            },
            "required": ["timezone", "allowedHoursLocal"],
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "consentSummary": {
        "type": "string",
        "minLength": 1,
        "maxLength": 280,
        "description": "Plain-English summary surfaced at the consent step. Should be short, action-oriented, and honest about money-touching surface (e.g. 'This skill can initiate payments up to $X/day to your pre-approved vendors')."
      },
      "trust": {
        "$ref": "_types.json#/$defs/trustTier",
        "description": "Trust tier. CODEOWNERS-protected — bumping community → verified requires TPA + 2 core reviewers."
      },
      "publisher": {
        "type": "string",
        "minLength": 1,
        "maxLength": 128,
        "description": "Org or individual maintaining the skill."
      },
      "iconPath": {
        "type": "string",
        "minLength": 1,
        "maxLength": 256,
        "default": "./icon.svg",
        "pattern": "^\\./[A-Za-z0-9._\\-/]+\\.(svg|png)$",
        "description": "Path to the skill icon, relative to the package root. SVG or PNG. Absolute paths and URLs are rejected."
      },
      "homepageUrl": {
        "$ref": "_types.json#/$defs/httpsUrl",
        "description": "Public homepage / docs URL. https-only."
      },
      "changelogUrl": {
        "$ref": "_types.json#/$defs/httpsUrl",
        "description": "Public changelog URL. https-only."
      },
      "packageVersion": {
        "$ref": "_types.json#/$defs/semver",
        "description": "SemVer of the skill package. Must match package.json version; CI verifies."
      },
      "blockingDependency": {
        "anyOf": [
          {
            "type": "string",
            "minLength": 1,
            "maxLength": 280
          },
          { "type": "null" }
        ],
        "description": "Free-text label for an upstream platform dependency that gates this skill (e.g. 'V3 DeFi integration'). The catalog surfaces 'wait for X' instead of 'install now' when set. Null = installable today."
      }
    },
    "required": [
      "schemaVersion",
      "slug",
      "displayName",
      "tagline",
      "longDescription",
      "category",
      "runtimeCompat",
      "requiredScopes",
      "policyTemplate",
      "consentSummary",
      "trust",
      "publisher",
      "iconPath",
      "packageVersion",
      "blockingDependency"
    ],
    "additionalProperties": false
  }
}
