From 71cd34860cc1b5d902d914c718cfcf83518f004b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 15 Jun 2026 22:24:13 +0000 Subject: [PATCH] feat(persona-kit): add side-effect-free ./spec validation entrypoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package barrel (index.js) re-exports the orchestration modules (mount, execute, plan, skills, skill-runner, config-files), so importing it to validate a persona/agent spec EVALUATES every one of those modules' top-level code — node:child_process, node:fs, and the deferred @relayfile/local-mount -> @parcel/watcher edge — none of which a pure validation consumer needs. Add `@agentworkforce/persona-kit/spec`: a narrow entrypoint that re-exports only the parsers, type guards, constants, and spec types from parse.ts / constants.ts / types.ts. The emitted dist/spec.js has ZERO external (non-relative) imports, so server-side validation consumers (e.g. AgentWorkforce Cloud's @cloud/core persona-spec wrapper) can pull in the validation surface without dragging in any native binding or orchestration dependency. Additive export only (the `.` barrel is unchanged); versioning is left to CI. Includes spec entrypoint tests. https://claude.ai/code/session_01CsCeMqBz8roJSvgpJeqCt7 --- packages/persona-kit/package.json | 4 + packages/persona-kit/src/spec.test.ts | 49 +++++++++++ packages/persona-kit/src/spec.ts | 115 ++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 packages/persona-kit/src/spec.test.ts create mode 100644 packages/persona-kit/src/spec.ts diff --git a/packages/persona-kit/package.json b/packages/persona-kit/package.json index df62f75a..6355aaba 100644 --- a/packages/persona-kit/package.json +++ b/packages/persona-kit/package.json @@ -10,6 +10,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, + "./spec": { + "types": "./dist/spec.d.ts", + "default": "./dist/spec.js" + }, "./package.json": "./package.json", "./schemas/persona.schema.json": "./schemas/persona.schema.json", "./schemas/agent.schema.json": "./schemas/agent.schema.json" diff --git a/packages/persona-kit/src/spec.test.ts b/packages/persona-kit/src/spec.test.ts new file mode 100644 index 00000000..6e390350 --- /dev/null +++ b/packages/persona-kit/src/spec.test.ts @@ -0,0 +1,49 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; + +import { + HARNESS_VALUES, + isHarness, + isIntent, + parseAgentSpec, + parsePersonaSpec +} from './spec.js'; + +function validSpec(over: Record = {}): Record { + return { + id: 'p', + intent: 'documentation', + tags: ['documentation'], + description: 'd', + harness: 'claude', + model: 'anthropic/claude-3-5-sonnet', + systemPrompt: 'be helpful', + harnessSettings: { reasoning: 'medium', timeoutSeconds: 300 }, + ...over + }; +} + +test('spec entrypoint re-exports the validation surface', () => { + assert.equal(typeof parsePersonaSpec, 'function'); + assert.equal(typeof parseAgentSpec, 'function'); + assert.equal(typeof isIntent, 'function'); + assert.equal(typeof isHarness, 'function'); + assert.ok(Array.isArray(HARNESS_VALUES)); +}); + +test('spec entrypoint validates a minimal persona spec', () => { + const spec = parsePersonaSpec(validSpec(), 'documentation'); + assert.equal(spec.id, 'p'); + assert.equal(spec.intent, 'documentation'); +}); + +test('spec entrypoint accepts the grok harness', () => { + assert.ok(HARNESS_VALUES.includes('grok')); + const spec = parsePersonaSpec(validSpec({ harness: 'grok' }), 'documentation'); + assert.equal(spec.harness, 'grok'); +}); + +test('spec entrypoint parses an agent spec', () => { + const agent = parseAgentSpec({}, 'agent'); + assert.equal(typeof agent, 'object'); +}); diff --git a/packages/persona-kit/src/spec.ts b/packages/persona-kit/src/spec.ts new file mode 100644 index 00000000..c3b5916c --- /dev/null +++ b/packages/persona-kit/src/spec.ts @@ -0,0 +1,115 @@ +/** + * Narrow, side-effect-free persona/agent spec VALIDATION surface. + * + * This entrypoint exists so server-side consumers that only need to + * validate a persona or agent spec (e.g. AgentWorkforce Cloud's deploy + * route, which routes persona validation through + * `@cloud/core/proactive-runtime/persona-spec.js`) can import the parsers + * and constants WITHOUT pulling in the orchestration modules the package + * barrel (`index.ts`) re-exports. + * + * The barrel re-exports `./mount.js`, `./execute.js`, `./plan.js`, + * `./skills.js`, `./skill-runner.js`, `./config-files.js`, etc. Importing + * the barrel therefore EVALUATES every one of those modules' top-level + * code — `node:child_process`, `node:fs`, and the deferred + * `@relayfile/local-mount` → `@parcel/watcher` edge — none of which a pure + * validation consumer needs. `./spec.js` transitively imports only + * `./parse.js`, `./constants.js`, and `./types.js`, which have ZERO + * external runtime dependencies (no `@relayfile/*`, no native bindings, + * no Node process/fs APIs at module-eval time). + * + * Keep this entrypoint in lockstep with the validation-related exports of + * `./index.js`; it is intentionally a strict subset, never a superset. + */ + +// Constants (intents, harnesses, permission modes, …) +export { + BUILT_IN_PERSONA_INTENTS, + CODEX_APPROVAL_POLICIES, + CODEX_SANDBOX_MODES, + HARNESS_SKILL_TARGETS, + HARNESS_VALUES, + PERMISSION_MODES, + PERSONA_INTENTS, + PERSONA_TAGS, + SIDECAR_MD_MODES, + SKILL_SOURCE_KINDS +} from './constants.js'; +export type { KnownPersonaTag } from './constants.js'; + +// Spec types +export type { + AgentSpec, + CapabilityValue, + CodexApprovalPolicy, + CodexSandboxMode, + Harness, + HarnessSettings, + HarnessSkillTarget, + IntegrationSource, + McpServerSpec, + PermissionMode, + PersonaInputPicker, + PersonaInputSpec, + PersonaIntegrationConfig, + PersonaIntegrationTrigger, + PersonaIntent, + PersonaAiMemoryConfig, + PersonaMemory, + PersonaMemoryConfig, + PersonaMemoryScope, + PersonaRelay, + PersonaRelayConfig, + PersonaTrajectoryConfig, + PersonaMount, + PersonaPermissions, + PersonaSchedule, + PersonaSelection, + PersonaSkill, + PersonaSpec, + PersonaTag, + ProactiveCapabilities, + SidecarMdMode, + SkillSourceKind, + WatchEvent, + WatchRule +} from './types.js'; + +// Parsers, type guards, and the sidecar resolver +export { + assertInputName, + assertSidecarPath, + deepFreeze, + INPUT_NAME_RE, + INTEGRATION_SOURCE_NAME_RE, + isHarness, + isIntent, + isObject, + isSidecarMode, + isTag, + parseAgentSpec, + parseCapabilities, + parseHarnessSettings, + parseInputs, + parseIntegrationConfig, + parseIntegrationSource, + parseIntegrationTrigger, + parseIntegrations, + parseMcpServers, + parseMemory, + parseMount, + parseOnEvent, + parseRelay, + parsePermissions, + parsePersonaSpec, + resolveAiMemory, + resolveTrajectoryRecording, + parseSchedules, + parseSkills, + parseStringList, + parseStringMap, + parseTags, + parseWatch, + resolveSidecar, + sidecarSelectionFields +} from './parse.js';