Copy page
View as Markdown View this page as plain text

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. Its sub-fields:

  • entryAgents declares callable entry agents.
  • effects (optional) lists packed effect names.
  • 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

Each packed agent’s definitions are isolated by package ID, preventing name collisions with host code or with 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;
}

Signatures are injected at load time, not embedded in source, so packed definitions remain portable.

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 mount packed thread endpoints beneath the same thread ID prefix used for local thread endpoints. Packed endpoints do not introduce a package-specific URL prefix.

{runtime_prefix}/{threadId}/{endpoint_path}

Requests beneath the thread endpoint prefix that do not match any local or packed thread endpoint route MUST return HTTP 404.

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;