@amalgm/agents 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/PURPOSE.md +29 -22
  2. package/README.md +36 -46
  3. package/dist/agents.d.ts +4 -26
  4. package/dist/agents.js +4 -87
  5. package/dist/bin/mcp.js +1 -1
  6. package/dist/bundles/create.d.ts +13 -0
  7. package/dist/bundles/create.js +125 -0
  8. package/dist/bundles/graph-records.d.ts +10 -0
  9. package/dist/bundles/graph-records.js +61 -0
  10. package/dist/bundles/graph.d.ts +6 -0
  11. package/dist/bundles/graph.js +121 -0
  12. package/dist/bundles/install.d.ts +6 -0
  13. package/dist/bundles/install.js +64 -0
  14. package/dist/bundles/types.d.ts +97 -0
  15. package/dist/bundles/types.js +1 -0
  16. package/dist/bundles/util.d.ts +14 -0
  17. package/dist/bundles/util.js +56 -0
  18. package/dist/bundles/validate.d.ts +2 -0
  19. package/dist/bundles/validate.js +92 -0
  20. package/dist/cli/help.d.ts +1 -1
  21. package/dist/cli/help.js +2 -13
  22. package/dist/cli/open.d.ts +1 -1
  23. package/dist/cli/open.js +1 -5
  24. package/dist/cli/run.js +3 -8
  25. package/dist/errors.d.ts +1 -1
  26. package/dist/errors.js +0 -2
  27. package/dist/http/bundle-routes.d.ts +2 -0
  28. package/dist/http/bundle-routes.js +67 -0
  29. package/dist/http/server.js +4 -8
  30. package/dist/http-types.d.ts +3 -0
  31. package/dist/index.d.ts +7 -2
  32. package/dist/index.js +5 -2
  33. package/dist/mcp/agent-tools.js +1 -1
  34. package/dist/mcp/server.js +1 -1
  35. package/dist/mcp/tools.js +1 -2
  36. package/dist/rows.d.ts +1 -4
  37. package/dist/rows.js +0 -39
  38. package/dist/schema.js +53 -101
  39. package/dist/types.d.ts +0 -106
  40. package/docs/ARCHITECTURE.md +21 -33
  41. package/docs/CLI.md +3 -43
  42. package/docs/DATA_MODEL.md +7 -46
  43. package/docs/DEFINITIONS.md +4 -4
  44. package/docs/ENGINE_INTEGRATION.md +22 -67
  45. package/docs/MCP.md +3 -19
  46. package/docs/REST.md +14 -85
  47. package/docs/SDK.md +12 -61
  48. package/docs/SECURITY.md +6 -35
  49. package/examples/basic.ts +8 -22
  50. package/package.json +2 -2
  51. package/skills/amalgm-agents/SKILL.md +3 -12
  52. package/skills/amalgm-agents/agents/openai.yaml +2 -2
  53. package/dist/cli/session-commands.d.ts +0 -4
  54. package/dist/cli/session-commands.js +0 -54
  55. package/dist/drivers.d.ts +0 -4
  56. package/dist/drivers.js +0 -25
  57. package/dist/event-store.d.ts +0 -13
  58. package/dist/event-store.js +0 -50
  59. package/dist/http/session-routes.d.ts +0 -2
  60. package/dist/http/session-routes.js +0 -67
  61. package/dist/http/stream.d.ts +0 -3
  62. package/dist/http/stream.js +0 -30
  63. package/dist/mcp/session-tools.d.ts +0 -3
  64. package/dist/mcp/session-tools.js +0 -81
  65. package/dist/messages.d.ts +0 -3
  66. package/dist/messages.js +0 -56
  67. package/dist/runtime.d.ts +0 -29
  68. package/dist/runtime.js +0 -176
  69. package/dist/session-store.d.ts +0 -15
  70. package/dist/session-store.js +0 -83
  71. package/dist/turn-store.d.ts +0 -31
  72. package/dist/turn-store.js +0 -164
  73. package/docs/DRIVERS.md +0 -72
@@ -0,0 +1,121 @@
1
+ import { actionEntryId, agentEntryId, appEntryId, automationEntryId, skillElementId, skillLabel, toolEntryId, } from './graph-records.js';
2
+ import { clean, object, uniqueStrings } from './util.js';
3
+ const ORDER = {
4
+ agent: 0, automation: 1, app: 2, tool: 3, tool_action: 4, skill: 5,
5
+ };
6
+ function elementId(type, id) {
7
+ return `${type}:${id}`;
8
+ }
9
+ function graphRecords(bundle) {
10
+ return {
11
+ agent: new Map(bundle.agents.map((entry) => [agentEntryId(entry), entry])),
12
+ automation: new Map(bundle.automations.map((entry) => [automationEntryId(entry), entry])),
13
+ app: new Map(bundle.apps.map((entry) => [appEntryId(entry), entry])),
14
+ tool: new Map(bundle.tools.map((entry) => [toolEntryId(entry), entry])),
15
+ tool_action: new Map(bundle.toolActions.map((entry) => [actionEntryId(entry), entry])),
16
+ skill: new Map(bundle.skills.map((entry) => [skillElementId(entry), entry])),
17
+ };
18
+ }
19
+ // Engine reference: amalgm-mcp/agent-bundles/graph.js buildFlatAgentBundleGraph.
20
+ export function buildBundleGraph(bundle) {
21
+ const records = graphRecords(bundle);
22
+ const elements = new Map();
23
+ const edges = new Map();
24
+ const addElement = (type, id, label) => {
25
+ const key = elementId(type, id);
26
+ elements.set(key, { id: key, type, label: label || id });
27
+ };
28
+ const addEdge = (fromType, fromId, toType, toId, kind) => {
29
+ const edge = { from: elementId(fromType, fromId), to: elementId(toType, toId), kind };
30
+ edges.set(`${edge.from}::${kind}::${edge.to}`, edge);
31
+ };
32
+ const requireRecord = (type, id, owner) => {
33
+ if (!records[type].has(id))
34
+ throw new Error(`${owner} references missing ${type.replace('_', ' ')}: ${id}`);
35
+ };
36
+ const addToolSelection = (ownerType, ownerId, selectedId) => {
37
+ if (records.tool_action.has(selectedId))
38
+ addEdge(ownerType, ownerId, 'tool_action', selectedId, 'uses-tool-action');
39
+ else {
40
+ requireRecord('tool', selectedId, `${ownerType} ${ownerId}`);
41
+ addEdge(ownerType, ownerId, 'tool', selectedId, 'uses-tool');
42
+ }
43
+ };
44
+ for (const entry of bundle.agents)
45
+ addElement('agent', agentEntryId(entry), entry.agent.name);
46
+ for (const entry of bundle.automations) {
47
+ addElement('automation', automationEntryId(entry), clean(object(entry.automation)?.name));
48
+ }
49
+ for (const entry of bundle.apps)
50
+ addElement('app', appEntryId(entry), clean(object(entry.app)?.name));
51
+ for (const entry of bundle.tools)
52
+ addElement('tool', toolEntryId(entry), clean(entry.name));
53
+ for (const entry of bundle.toolActions)
54
+ addElement('tool_action', actionEntryId(entry), clean(entry.name));
55
+ for (const skill of bundle.skills) {
56
+ const id = skillElementId(skill);
57
+ elements.set(id, { id, type: 'skill', label: skillLabel(skill) });
58
+ }
59
+ for (const entry of bundle.agents) {
60
+ const id = agentEntryId(entry);
61
+ for (const subagent of entry.config.subagents) {
62
+ if (!subagent.agentId)
63
+ continue;
64
+ requireRecord('agent', subagent.agentId, `agent ${id}`);
65
+ addEdge('agent', id, 'agent', subagent.agentId, 'uses-subagent');
66
+ }
67
+ for (const selectedId of entry.config.loadout.toolIds)
68
+ addToolSelection('agent', id, selectedId);
69
+ for (const skill of entry.config.skills) {
70
+ const skillId = skillElementId(skill);
71
+ if (!records.skill.has(skillId))
72
+ throw new Error(`agent ${id} references missing skill: ${skillLabel(skill)}`);
73
+ addEdge('agent', id, 'skill', skillId.slice('skill:'.length), 'uses-skill');
74
+ }
75
+ }
76
+ for (const entry of bundle.automations) {
77
+ const id = automationEntryId(entry);
78
+ for (const selectedId of uniqueStrings(entry.toolIds))
79
+ addToolSelection('automation', id, selectedId);
80
+ }
81
+ for (const entry of bundle.apps) {
82
+ const id = appEntryId(entry);
83
+ for (const toolId of uniqueStrings(entry.toolIds)) {
84
+ requireRecord('tool', toolId, `app ${id}`);
85
+ addEdge('app', id, 'tool', toolId, 'provides-tool');
86
+ }
87
+ }
88
+ for (const tool of bundle.tools) {
89
+ const id = toolEntryId(tool);
90
+ const appId = clean(tool.appId);
91
+ if (appId) {
92
+ requireRecord('app', appId, `tool ${id}`);
93
+ addEdge('tool', id, 'app', appId, 'requires-app');
94
+ }
95
+ }
96
+ for (const action of bundle.toolActions) {
97
+ const id = actionEntryId(action);
98
+ const toolId = clean(action.toolId);
99
+ requireRecord('tool', toolId, `tool action ${id}`);
100
+ addEdge('tool', toolId, 'tool_action', id, 'has-action');
101
+ addEdge('tool_action', id, 'tool', toolId, 'action-of');
102
+ const appId = clean(action.appId);
103
+ if (appId) {
104
+ requireRecord('app', appId, `tool action ${id}`);
105
+ addEdge('tool_action', id, 'app', appId, 'requires-app');
106
+ }
107
+ }
108
+ const headIds = bundle.heads.map((head) => {
109
+ const id = elementId(head.type, head.id);
110
+ if (!elements.has(id))
111
+ throw new Error(`bundle head references missing element: ${id}`);
112
+ return id;
113
+ });
114
+ return {
115
+ headIds,
116
+ elements: [...elements.values()].sort((left, right) => (ORDER[left.type] ?? 99) - (ORDER[right.type] ?? 99)
117
+ || left.id.localeCompare(right.id)),
118
+ edges: [...edges.values()].sort((left, right) => left.from.localeCompare(right.from)
119
+ || left.kind.localeCompare(right.kind) || left.to.localeCompare(right.to)),
120
+ };
121
+ }
@@ -0,0 +1,6 @@
1
+ import type { Agents } from '../agents.js';
2
+ import type { AgentBundlePort, BundleInstallResult } from './types.js';
3
+ export declare function installAgentBundle(agents: Agents, input: unknown, options?: {
4
+ port?: AgentBundlePort;
5
+ authRef?: string;
6
+ }): Promise<BundleInstallResult>;
@@ -0,0 +1,64 @@
1
+ import crypto from 'node:crypto';
2
+ import { normalizeAgentConfig } from '../config/schema.js';
3
+ import { updateAgentConfig } from '../config/store.js';
4
+ import { validateAgentBundle } from './validate.js';
5
+ function uniqueName(agents, value) {
6
+ const base = value.trim() || 'Imported agent';
7
+ const names = new Set(agents.listAgents().map((item) => item.definition.name.toLowerCase()));
8
+ if (!names.has(base.toLowerCase()))
9
+ return base;
10
+ if (!names.has(`${base} (imported)`.toLowerCase()))
11
+ return `${base} (imported)`;
12
+ for (let index = 2; index < 1000; index += 1) {
13
+ const candidate = `${base} (imported ${index})`;
14
+ if (!names.has(candidate.toLowerCase()))
15
+ return candidate;
16
+ }
17
+ return `${base} (${crypto.randomUUID().slice(0, 8)})`;
18
+ }
19
+ // Engine reference: amalgm-mcp/agent-bundles/install.js installBundleAgents.
20
+ export async function installAgentBundle(agents, input, options = {}) {
21
+ const bundle = validateAgentBundle(input);
22
+ const hasExternal = bundle.automations.length || bundle.apps.length || bundle.tools.length;
23
+ if (hasExternal && !options.port)
24
+ throw new Error('A bundle resource port is required to install external resources');
25
+ const external = options.port ? await options.port.installResources(bundle) : {};
26
+ const idMap = new Map();
27
+ const created = [];
28
+ for (const entry of bundle.agents) {
29
+ const agent = agents.createAgent({
30
+ id: `imported-${crypto.randomUUID()}`,
31
+ name: uniqueName(agents, entry.agent.name),
32
+ description: entry.agent.description,
33
+ driver: { id: entry.agent.driverId, config: {} },
34
+ model: entry.agent.modelId ? { id: entry.agent.modelId, settings: entry.agent.modelSettings || {} } : null,
35
+ authRef: options.authRef || null,
36
+ instructions: entry.config.instructions,
37
+ resources: { files: [], skills: [], subagents: [] },
38
+ toolbox: { toolIds: entry.config.loadout.toolIds, actionIds: [] },
39
+ metadata: { installedFromBundle: bundle.bundleId },
40
+ });
41
+ idMap.set(entry.sourceAgentId, agent.id);
42
+ created.push({ sourceId: entry.sourceAgentId, agent, config: entry.config });
43
+ }
44
+ const installedAgents = created.map(({ agent, config }) => {
45
+ const rewritten = normalizeAgentConfig({
46
+ ...config,
47
+ agentId: agent.id,
48
+ subagents: config.subagents.map((subagent) => ({
49
+ ...subagent,
50
+ agentId: idMap.get(subagent.agentId) || '',
51
+ })).filter((subagent) => subagent.agentId),
52
+ });
53
+ return updateAgentConfig(agents, agent.id, rewritten).agent;
54
+ });
55
+ return {
56
+ ok: true,
57
+ bundleId: bundle.bundleId,
58
+ rootAgentId: bundle.rootAgentId ? idMap.get(bundle.rootAgentId) || null : installedAgents[0]?.id || null,
59
+ idMap: Object.fromEntries(idMap),
60
+ installedAgents,
61
+ ...external,
62
+ warnings: [...(bundle.warnings || []), ...(external.warnings || [])],
63
+ };
64
+ }
@@ -0,0 +1,97 @@
1
+ import type { AgentConfig } from '../config/types.js';
2
+ import type { AgentRecord, JsonObject } from '../types.js';
3
+ export type BundleHeadType = 'agent' | 'automation' | 'app' | 'tool';
4
+ export interface BundleHead {
5
+ type: BundleHeadType;
6
+ id: string;
7
+ }
8
+ export interface BundleAgentEntry {
9
+ sourceAgentId: string;
10
+ agent: {
11
+ name: string;
12
+ description: string;
13
+ driverId: string;
14
+ modelId: string;
15
+ modelSettings: JsonObject | null;
16
+ };
17
+ config: AgentConfig;
18
+ }
19
+ export interface BundleGraphElement {
20
+ id: string;
21
+ type: string;
22
+ label: string;
23
+ }
24
+ export interface BundleGraphEdge {
25
+ from: string;
26
+ to: string;
27
+ kind: string;
28
+ }
29
+ export interface BundleRequirements {
30
+ runtimes: string[];
31
+ auth: string[];
32
+ secrets: string[];
33
+ bindings: string[];
34
+ systemTools: string[];
35
+ systemActions: string[];
36
+ missingTools: string[];
37
+ }
38
+ export interface AgentBundle {
39
+ kind: 'amalgm.bundle';
40
+ schemaVersion: 2;
41
+ bundleId: string;
42
+ createdAt: string;
43
+ rootAgentId: string | null;
44
+ heads: BundleHead[];
45
+ headIds: string[];
46
+ title: string;
47
+ description: string;
48
+ agents: BundleAgentEntry[];
49
+ automations: JsonObject[];
50
+ apps: JsonObject[];
51
+ tools: JsonObject[];
52
+ toolActions: JsonObject[];
53
+ skills: JsonObject[];
54
+ assets: JsonObject[];
55
+ requires: BundleRequirements;
56
+ warnings: string[];
57
+ summary: JsonObject;
58
+ elements: BundleGraphElement[];
59
+ edges: BundleGraphEdge[];
60
+ }
61
+ export interface BundleExternalExport {
62
+ headToolIds?: string[];
63
+ automations?: JsonObject[];
64
+ apps?: JsonObject[];
65
+ tools?: JsonObject[];
66
+ toolActions?: JsonObject[];
67
+ requires?: Partial<BundleRequirements>;
68
+ warnings?: string[];
69
+ }
70
+ export interface BundleExternalInstall {
71
+ installedAutomations?: JsonObject[];
72
+ installedApps?: JsonObject[];
73
+ installedTools?: JsonObject[];
74
+ warnings?: string[];
75
+ }
76
+ export interface AgentBundlePort {
77
+ exportResources(input: {
78
+ automationIds: string[];
79
+ appIds: string[];
80
+ toolIds: string[];
81
+ }): Promise<BundleExternalExport> | BundleExternalExport;
82
+ installResources(bundle: AgentBundle): Promise<BundleExternalInstall> | BundleExternalInstall;
83
+ }
84
+ export interface BundleCreateInput {
85
+ agentIds?: string[];
86
+ automationIds?: string[];
87
+ appIds?: string[];
88
+ toolIds?: string[];
89
+ }
90
+ export interface BundleInstallResult extends BundleExternalInstall {
91
+ ok: true;
92
+ bundleId: string;
93
+ rootAgentId: string | null;
94
+ idMap: Record<string, string>;
95
+ installedAgents: AgentRecord[];
96
+ warnings: string[];
97
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { JsonObject, JsonValue } from '../types.js';
2
+ import type { BundleRequirements } from './types.js';
3
+ export declare const BUNDLE_KIND: "amalgm.bundle";
4
+ export declare const BUNDLE_SCHEMA_VERSION: 2;
5
+ export declare function clean(value: unknown): string;
6
+ export declare function object(value: unknown): JsonObject | null;
7
+ export declare function clone<T>(value: T): T;
8
+ export declare function uniqueStrings(value: unknown): string[];
9
+ export declare function addUnique(target: string[], value: string): void;
10
+ export declare function bundleId(): string;
11
+ export declare function digest(value: unknown): string;
12
+ export declare function emptyRequirements(): BundleRequirements;
13
+ export declare function mergeRequirements(target: BundleRequirements, incoming?: Partial<BundleRequirements>): BundleRequirements;
14
+ export declare function scrubSecrets(value: JsonValue, path: string, requires: BundleRequirements): JsonValue;
@@ -0,0 +1,56 @@
1
+ import crypto from 'node:crypto';
2
+ export const BUNDLE_KIND = 'amalgm.bundle';
3
+ export const BUNDLE_SCHEMA_VERSION = 2;
4
+ export function clean(value) {
5
+ return typeof value === 'string' ? value.trim() : '';
6
+ }
7
+ export function object(value) {
8
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
9
+ }
10
+ export function clone(value) {
11
+ return JSON.parse(JSON.stringify(value));
12
+ }
13
+ export function uniqueStrings(value) {
14
+ if (!Array.isArray(value))
15
+ return [];
16
+ return [...new Set(value.map(clean).filter(Boolean))];
17
+ }
18
+ export function addUnique(target, value) {
19
+ if (value && !target.includes(value))
20
+ target.push(value);
21
+ }
22
+ export function bundleId() {
23
+ return `agent-bundle-${crypto.randomUUID()}`;
24
+ }
25
+ export function digest(value) {
26
+ return crypto.createHash('sha256').update(typeof value === 'string' ? value : JSON.stringify(value))
27
+ .digest('hex').slice(0, 16);
28
+ }
29
+ export function emptyRequirements() {
30
+ return { runtimes: [], auth: [], secrets: [], bindings: [], systemTools: [],
31
+ systemActions: [], missingTools: [] };
32
+ }
33
+ export function mergeRequirements(target, incoming = {}) {
34
+ for (const key of Object.keys(target)) {
35
+ for (const value of incoming[key] || [])
36
+ addUnique(target[key], value);
37
+ }
38
+ return target;
39
+ }
40
+ const SECRET = /(api[_-]?key|authorization|bearer|cookie|password|passwd|secret|token|credential|private[_-]?key)/i;
41
+ export function scrubSecrets(value, path, requires) {
42
+ if (Array.isArray(value))
43
+ return value.map((item, index) => scrubSecrets(item, `${path}[${index}]`, requires));
44
+ const record = object(value);
45
+ if (!record)
46
+ return value;
47
+ const output = {};
48
+ for (const [key, child] of Object.entries(record)) {
49
+ const childPath = path ? `${path}.${key}` : key;
50
+ if (SECRET.test(key))
51
+ addUnique(requires.secrets, childPath);
52
+ else
53
+ output[key] = scrubSecrets(child, childPath, requires);
54
+ }
55
+ return output;
56
+ }
@@ -0,0 +1,2 @@
1
+ import type { AgentBundle } from './types.js';
2
+ export declare function validateAgentBundle(input: unknown): AgentBundle;
@@ -0,0 +1,92 @@
1
+ import { actionEntryId, agentEntryId, appEntryId, automationEntryId, skillFingerprint, toolEntryId } from './graph-records.js';
2
+ import { buildBundleGraph } from './graph.js';
3
+ import { BUNDLE_KIND, BUNDLE_SCHEMA_VERSION, clean, object } from './util.js';
4
+ function unique(label, records, identify) {
5
+ const seen = new Set();
6
+ for (const record of records) {
7
+ const id = identify(record);
8
+ if (!id)
9
+ throw new Error(`${label} entries must include ids`);
10
+ if (seen.has(id))
11
+ throw new Error(`Bundle contains duplicate ${label} id: ${id}`);
12
+ seen.add(id);
13
+ }
14
+ }
15
+ function assertClosed(bundle) {
16
+ const elements = new Set();
17
+ for (const element of bundle.elements) {
18
+ if (!clean(element.id) || !clean(element.type))
19
+ throw new Error('bundle graph elements must include id and type');
20
+ if (object(element)?.data !== undefined)
21
+ throw new Error(`bundle graph element must not copy data: ${element.id}`);
22
+ if (elements.has(element.id))
23
+ throw new Error(`bundle graph contains duplicate element id: ${element.id}`);
24
+ elements.add(element.id);
25
+ }
26
+ const outgoing = new Map();
27
+ const edgeKeys = new Set();
28
+ for (const edge of bundle.edges) {
29
+ if (!elements.has(edge.from) || !elements.has(edge.to))
30
+ throw new Error('bundle graph edge references missing element');
31
+ const key = `${edge.from}::${edge.kind}::${edge.to}`;
32
+ if (edgeKeys.has(key))
33
+ throw new Error(`bundle graph contains duplicate edge: ${key}`);
34
+ edgeKeys.add(key);
35
+ outgoing.set(edge.from, [...(outgoing.get(edge.from) || []), edge.to]);
36
+ }
37
+ const reachable = new Set();
38
+ const pending = [...bundle.headIds];
39
+ while (pending.length) {
40
+ const id = pending.pop();
41
+ if (reachable.has(id))
42
+ continue;
43
+ reachable.add(id);
44
+ pending.push(...(outgoing.get(id) || []));
45
+ }
46
+ const unreachable = [...elements].filter((id) => !reachable.has(id));
47
+ if (unreachable.length)
48
+ throw new Error(`bundle graph contains unreachable elements: ${unreachable.join(', ')}`);
49
+ }
50
+ // Engine reference: amalgm-mcp/agent-bundles/install.js validateBundle.
51
+ export function validateAgentBundle(input) {
52
+ const raw = object(input);
53
+ if (!raw)
54
+ throw new Error('bundle must be an object');
55
+ if (raw.kind !== BUNDLE_KIND)
56
+ throw new Error(`Unsupported bundle kind: ${clean(raw.kind) || 'unknown'}`);
57
+ if (raw.schemaVersion !== BUNDLE_SCHEMA_VERSION) {
58
+ throw new Error(`Unsupported bundle schema version: ${String(raw.schemaVersion || 'unknown')}`);
59
+ }
60
+ const bundle = input;
61
+ const arrays = ['agents', 'automations', 'apps', 'tools', 'toolActions', 'skills', 'heads'];
62
+ if (arrays.some((key) => !Array.isArray(bundle[key])))
63
+ throw new Error('bundle canonical records must be arrays');
64
+ if (!bundle.heads.length)
65
+ throw new Error('bundle must include at least one head');
66
+ unique('agent', bundle.agents, agentEntryId);
67
+ unique('automation', bundle.automations, automationEntryId);
68
+ unique('app', bundle.apps, appEntryId);
69
+ unique('tool', bundle.tools, toolEntryId);
70
+ unique('tool action', bundle.toolActions, actionEntryId);
71
+ unique('skill', bundle.skills, skillFingerprint);
72
+ unique('head', bundle.heads, (value) => {
73
+ const head = object(value);
74
+ return ['agent', 'automation', 'app', 'tool'].includes(clean(head?.type))
75
+ ? `${clean(head?.type)}:${clean(head?.id)}` : '';
76
+ });
77
+ if (bundle.requires?.missingTools?.length) {
78
+ throw new Error(`Bundle is missing tools: ${bundle.requires.missingTools.join(', ')}`);
79
+ }
80
+ const expected = buildBundleGraph(bundle);
81
+ const expectedRoot = bundle.heads.find((head) => head.type === 'agent')?.id || null;
82
+ if (bundle.rootAgentId !== expectedRoot) {
83
+ throw new Error('bundle rootAgentId does not match its canonical records and connections');
84
+ }
85
+ for (const key of ['headIds', 'elements', 'edges']) {
86
+ if (JSON.stringify(bundle[key]) !== JSON.stringify(expected[key])) {
87
+ throw new Error(`bundle ${key} does not match its canonical records and connections`);
88
+ }
89
+ }
90
+ assertClosed(bundle);
91
+ return bundle;
92
+ }
@@ -1 +1 @@
1
- export declare const HELP = "amalgm-agents \u2014 local agent definitions and durable sessions\n\nAgent definitions:\n amalgm-agents agent list [--include-deleted]\n amalgm-agents agent show <id>\n amalgm-agents agent create <definition.json>\n amalgm-agents agent update <id> <patch.json>\n amalgm-agents agent delete <id>\n\nSessions:\n amalgm-agents session list [--agent <id>] [--include-archived]\n amalgm-agents session start <agent-id> [--id <id>] [--revision <id>]\n amalgm-agents session show <id>\n amalgm-agents session events <id> [--after <sequence>]\n amalgm-agents session send <id> <message> [--key <idempotency-key>]\n amalgm-agents session cancel <id>\n amalgm-agents session archive <id>\n amalgm-agents talk <agent-id> <message> [--session <id>] [--background]\n\nGlobal options:\n --state-dir <path> Agents state directory\n --drivers <a.js,b.js> Driver modules (or AMALGM_AGENT_DRIVERS)\n --help Show this help\n";
1
+ export declare const HELP = "amalgm-agents \u2014 local agent identities and immutable revisions\n\nAgents:\n amalgm-agents agent list [--include-deleted]\n amalgm-agents agent show <id>\n amalgm-agents agent create <definition.json>\n amalgm-agents agent update <id> <patch.json>\n amalgm-agents agent delete <id>\n\nGlobal options:\n --state-dir <path> Agents state directory\n --help Show this help\n";
package/dist/cli/help.js CHANGED
@@ -1,24 +1,13 @@
1
- export const HELP = `amalgm-agents — local agent definitions and durable sessions
1
+ export const HELP = `amalgm-agents — local agent identities and immutable revisions
2
2
 
3
- Agent definitions:
3
+ Agents:
4
4
  amalgm-agents agent list [--include-deleted]
5
5
  amalgm-agents agent show <id>
6
6
  amalgm-agents agent create <definition.json>
7
7
  amalgm-agents agent update <id> <patch.json>
8
8
  amalgm-agents agent delete <id>
9
9
 
10
- Sessions:
11
- amalgm-agents session list [--agent <id>] [--include-archived]
12
- amalgm-agents session start <agent-id> [--id <id>] [--revision <id>]
13
- amalgm-agents session show <id>
14
- amalgm-agents session events <id> [--after <sequence>]
15
- amalgm-agents session send <id> <message> [--key <idempotency-key>]
16
- amalgm-agents session cancel <id>
17
- amalgm-agents session archive <id>
18
- amalgm-agents talk <agent-id> <message> [--session <id>] [--background]
19
-
20
10
  Global options:
21
11
  --state-dir <path> Agents state directory
22
- --drivers <a.js,b.js> Driver modules (or AMALGM_AGENT_DRIVERS)
23
12
  --help Show this help
24
13
  `;
@@ -1,3 +1,3 @@
1
1
  import { Agents } from '../agents.js';
2
2
  import type { ParsedArgs } from './args.js';
3
- export declare function openAgents(args: ParsedArgs): Promise<Agents>;
3
+ export declare function openAgents(args: ParsedArgs): Agents;
package/dist/cli/open.js CHANGED
@@ -1,12 +1,8 @@
1
1
  import { Agents } from '../agents.js';
2
- import { driverSpecifiers, loadDriverModules } from '../drivers.js';
3
2
  import { flag } from './args.js';
4
- export async function openAgents(args) {
5
- const modules = flag(args, 'drivers');
3
+ export function openAgents(args) {
6
4
  const stateDir = flag(args, 'state-dir');
7
- const drivers = await loadDriverModules(driverSpecifiers(modules));
8
5
  return new Agents({
9
- drivers,
10
6
  ...(stateDir ? { stateDir } : {}),
11
7
  });
12
8
  }
package/dist/cli/run.js CHANGED
@@ -4,7 +4,6 @@ import { runAgentCommand } from './agent-commands.js';
4
4
  import { writeJson } from './files.js';
5
5
  import { HELP } from './help.js';
6
6
  import { openAgents } from './open.js';
7
- import { runSessionCommand, runTalkCommand } from './session-commands.js';
8
7
  export async function runCli(argv = process.argv.slice(2)) {
9
8
  const args = parseArgs(argv);
10
9
  if (args.flags.has('help') || args.words.length === 0) {
@@ -13,15 +12,11 @@ export async function runCli(argv = process.argv.slice(2)) {
13
12
  }
14
13
  let agents;
15
14
  try {
16
- agents = await openAgents(args);
15
+ agents = openAgents(args);
17
16
  const command = args.words[0];
18
17
  const value = command === 'agent'
19
18
  ? runAgentCommand(agents, args)
20
- : command === 'session'
21
- ? await runSessionCommand(agents, args)
22
- : command === 'talk'
23
- ? await runTalkCommand(agents, args)
24
- : undefined;
19
+ : undefined;
25
20
  if (value === undefined)
26
21
  throw new Error(`Unknown command: ${String(command)}`);
27
22
  writeJson(value);
@@ -33,6 +28,6 @@ export async function runCli(argv = process.argv.slice(2)) {
33
28
  return 1;
34
29
  }
35
30
  finally {
36
- await agents?.close();
31
+ agents?.close();
37
32
  }
38
33
  }
package/dist/errors.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { JsonObject } from './types.js';
2
- export type AgentErrorCode = 'invalid_input' | 'not_found' | 'conflict' | 'deleted' | 'driver_unavailable' | 'too_large' | 'cancelled' | 'internal';
2
+ export type AgentErrorCode = 'invalid_input' | 'not_found' | 'conflict' | 'deleted' | 'too_large' | 'internal';
3
3
  export declare class AgentError extends Error {
4
4
  readonly code: AgentErrorCode;
5
5
  readonly details: JsonObject;
package/dist/errors.js CHANGED
@@ -3,9 +3,7 @@ const statusByCode = {
3
3
  not_found: 404,
4
4
  conflict: 409,
5
5
  deleted: 410,
6
- driver_unavailable: 503,
7
6
  too_large: 413,
8
- cancelled: 409,
9
7
  internal: 500,
10
8
  };
11
9
  export class AgentError extends Error {
@@ -0,0 +1,2 @@
1
+ import type { RouteContext } from '../http-types.js';
2
+ export declare function routeAgentBundles(context: RouteContext): Promise<boolean>;
@@ -0,0 +1,67 @@
1
+ import { createAgentBundle } from '../bundles/create.js';
2
+ import { installAgentBundle } from '../bundles/install.js';
3
+ import { BUNDLE_KIND, BUNDLE_SCHEMA_VERSION } from '../bundles/util.js';
4
+ import { scanInstalledSkills } from '../skills/scanner.js';
5
+ function ids(...values) {
6
+ const result = [];
7
+ for (const value of values) {
8
+ if (typeof value === 'string' && value.trim())
9
+ result.push(value.trim());
10
+ if (Array.isArray(value))
11
+ value.forEach((item) => {
12
+ if (typeof item === 'string' && item.trim())
13
+ result.push(item.trim());
14
+ });
15
+ }
16
+ return [...new Set(result)];
17
+ }
18
+ export async function routeAgentBundles(context) {
19
+ const [resource, action, child] = context.path;
20
+ if (resource !== 'agent-bundles')
21
+ return false;
22
+ if (!action && context.method === 'GET') {
23
+ context.json(200, {
24
+ kind: BUNDLE_KIND,
25
+ schemaVersion: BUNDLE_SCHEMA_VERSION,
26
+ available: { agents: context.agents.listAgents().map((agent) => ({
27
+ id: agent.id, name: agent.definition.name, description: agent.definition.description,
28
+ })) },
29
+ });
30
+ return true;
31
+ }
32
+ if (child || context.method !== 'POST')
33
+ return false;
34
+ const body = await context.body();
35
+ try {
36
+ if (action === 'preview') {
37
+ const installedSkills = scanInstalledSkills({ ...context.skillRoots, includeContent: true }).skills;
38
+ const result = await createAgentBundle(context.agents, {
39
+ agentIds: ids(body.agent_id, body.agent_ids),
40
+ automationIds: ids(body.automation_id, body.automation_ids),
41
+ appIds: ids(body.app_id, body.app_ids),
42
+ toolIds: ids(body.tool_id, body.tool_ids),
43
+ }, { ...(context.bundlePort ? { port: context.bundlePort } : {}), installedSkills });
44
+ context.json(200, { ok: true, ...result });
45
+ return true;
46
+ }
47
+ if (action === 'install') {
48
+ if (!body.bundle) {
49
+ context.json(400, { error: 'bundle is required' });
50
+ return true;
51
+ }
52
+ const authRef = typeof body.auth_ref === 'string' ? body.auth_ref
53
+ : typeof body.authMethod === 'string' ? body.authMethod : undefined;
54
+ const result = await installAgentBundle(context.agents, body.bundle, {
55
+ ...(context.bundlePort ? { port: context.bundlePort } : {}),
56
+ ...(authRef ? { authRef } : {}),
57
+ });
58
+ context.json(200, result);
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ catch (error) {
64
+ context.json(400, { error: error instanceof Error ? error.message : 'Bundle operation failed' });
65
+ return true;
66
+ }
67
+ }