Packaging

Standard Agents can be packaged into self-contained npm packages, making them installable and distributable. Each packed agent includes all its dependencies (prompts, tools, models, hooks, effects) in a single module.

Entry Point Exports

The package entry point exports named registries for each definition type, plus a __meta export with package metadata:

import type {
  AgentDefinition,
  PromptDefinition,
  ToolDefinition,
  ModelDefinition,
  PackedMeta,
} from '@standardagents/spec';

export const agents = {
  my_agent: async (): Promise<AgentDefinition> => ({
    name: 'my_agent',
    type: 'ai_human',
    title: 'My Agent',
    sideA: { prompt: 'main_prompt' },
  }),
} as const;

export const prompts = {
  main_prompt: async (): Promise<PromptDefinition> => ({
    name: 'main_prompt',
    model: 'gpt_4o',
    prompt: 'You are a helpful assistant...',
    tools: ['search'],
  }),
} as const;

export const tools = {
  search: async (): Promise<ToolDefinition> => ({
    description: 'Search for information',
    execute: async (state, args) => {
      return { status: 'success', result: 'Found results' };
    },
  }),
} as const;

export const models = {
  gpt_4o: async (): Promise<ModelDefinition> => ({
    name: 'gpt_4o',
    model: 'gpt-4o',
    provider: 'openai',
  }),
} as const;

export const hooks = {
  limit_messages: async () => ({
    hook: 'filter_messages' as const,
    id: 'limit_messages',
    execute: async (state: any, messages: any[]) => messages.slice(-50),
  }),
} as const;

export const effects = {
  send_digest: async () => ([
    'Send a digest email',
    null,
    async (state: any) => {
      // effect work
    },
  ]),
} as const;

export const __meta: PackedMeta = {
  packageId: 'standardagent-my-workflow',
  version: '1.0.0',
  entryAgents: ['my_agent'],
  packedAt: 1705012800000,
};

Lazy Loading

Every definition is wrapped in an async function (DefinitionLoader<T>) that returns a Promise. This enables tree-shaking, faster startup, and on-demand loading of large tool implementations.

type DefinitionLoader<T> = () => Promise<T>;

// Usage
const searchTool = await tools.search();

PackedExports Interface

For type-safe package consumption:

import type { PackedExports } from '@standardagents/spec';

const pkg: PackedExports = await import('standardagent-my-workflow');
const meta = pkg.__meta;
const agent = await pkg.agents.my_agent();

Full interface:

interface PackedExports {
  agents: Record<string, DefinitionLoader<AgentDefinition>>;
  prompts: Record<string, DefinitionLoader<PromptDefinition>>;
  tools: Record<string, DefinitionLoader<ToolDefinition>>;
  models: Record<string, DefinitionLoader<ModelDefinition>>;
  hooks: Record<string, DefinitionLoader<HookDefinitionResult>>;
  effects?: Record<string, DefinitionLoader<EffectDefinition<any, any>>>;
  threadEndpoints?: Record<string, DefinitionLoader<MarkedThreadEndpoint | Controller>>;
  __meta: PackedMeta;
}

package.json Requirements

Packed agents are identified by the standardagent field in package.json:

{
  "name": "standardagent-my-workflow",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  },
  "keywords": ["standardagent"],
  "standardagent": {
    "entryAgents": ["my_agent"],
    "effects": ["send_digest", "cleanup"],
    "threadEndpoints": ["status.get", "admin/sync.post"]
  },
  "peerDependencies": {
    "@standardagents/spec": ">=0.1.0"
  }
}

The standardagent field marks this as a Standard Agent package. entryAgents declares callable entry agents, effects (optional) lists packed effect names, and threadEndpoints (optional) lists packed thread endpoint keys. The keywords array MUST include "standardagent" for discovery.

PackedMeta

The __meta export provides package information at runtime:

export interface PackedMeta {
  /** Unique package identifier (npm name or local identifier) */
  packageId: string;
  /** Package version (semver) */
  version: string;
  /** Entry agents exposed as tools (visible from outside) */
  entryAgents: string[];
  /** When the package was packed (timestamp in milliseconds) */
  packedAt: number;
  /** Optional description */
  description?: string;
}

Namespace Isolation

Packed agents operate in isolated namespaces, preventing conflicts with the host application and other packages.

Visibility Rules

From \ ToUnpackedSame PackageEntry AgentsOther Package Internals
GlobalYes-YesNo
PackedNoYesYesNo
  • Global code sees unpacked definitions and packed entry points
  • Packed code sees only its own package contents plus other entry points
  • Internal tools/prompts in a package are hidden from outside

Package Signature

Each packed definition receives a signature marking its package membership:

interface PackageSignature {
  packageId: string;
  version: string;
  source: 'npm' | 'local';
  packedAt: number;
}

The signature is injected at load time (not baked into source), keeping the packed source clean.

Discovery

Runtimes discover packed agents by:

  1. Scanning node_modules for packages with "standardagent" in package.json
  2. Checking local agents/packed/ directory for development packages
  3. Importing each package and reading __meta for configuration

Naming Conventions

  • Package names SHOULD start with standardagent-
  • Agent, tool, prompt, hook, and effect names use snake_case
  • Only expose agents that should be callable from outside as entry points
  • Internal helper agents SHOULD NOT be entry points

Packed Thread Endpoints

Packed thread endpoints are exported under threadEndpoints using endpoint keys like status.get or admin/sync.post.

Runtimes should mount these under a package namespace to avoid collisions, for example:

/api/packages/{packageId}/threads/{threadId}/...

Type Definitions

Packed agents include TypeScript definitions for type-safe consumption:

// dist/index.d.ts
import type {
  AgentDefinition,
  PromptDefinition,
  ToolDefinition,
  ModelDefinition,
  Controller,
  EffectDefinition,
  HookDefinitionResult,
  MarkedThreadEndpoint,
  PackedMeta,
} from '@standardagents/spec';

type DefinitionLoader<T> = () => Promise<T>;

export declare const agents: {
  readonly my_agent: DefinitionLoader<AgentDefinition>;
};

export declare const prompts: {
  readonly main_prompt: DefinitionLoader<PromptDefinition>;
};

export declare const tools: {
  readonly search: DefinitionLoader<ToolDefinition>;
};

export declare const models: {
  readonly gpt_4o: DefinitionLoader<ModelDefinition>;
};

export declare const hooks: {
  readonly limit_messages: DefinitionLoader<HookDefinitionResult>;
};

export declare const effects: {
  readonly send_digest: DefinitionLoader<EffectDefinition<any, any>>;
};

export declare const threadEndpoints: {
  readonly "status.get": DefinitionLoader<MarkedThreadEndpoint | Controller>;
};

export declare const __meta: PackedMeta;