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:
entryAgentsdeclares 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 \ To | Unpacked | Same Package | Entry Agents | Other Package Internals |
|---|---|---|---|---|
| Global | Yes | - | Yes | No |
| Packed | No | Yes | Yes | No |
- 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:
- Scanning
node_modulesfor packages with"standardagent"inpackage.json - Checking local
agents/packed/directory for development packages - Importing each package and reading
__metafor 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;