# 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:

```typescript
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.

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

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

### PackedExports Interface

For type-safe package consumption:

```typescript
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:

```typescript
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`:

```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:

```typescript
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:

```typescript
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.

```text
{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:

```typescript
// 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;
```