@amalgm/agents 0.1.1 → 0.1.3

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ import type { RouteContext } from '../http-types.js';
2
+ export declare function routeAgentConfig(context: RouteContext): Promise<boolean>;
@@ -0,0 +1,46 @@
1
+ import { isObject } from '../json.js';
2
+ import { importAgentConfig } from '../config/import.js';
3
+ import { configFromAgent, listAgentConfigs, updateAgentConfig } from '../config/store.js';
4
+ function agentId(body) {
5
+ return typeof body.agent_id === 'string' ? body.agent_id.trim() : '';
6
+ }
7
+ export async function routeAgentConfig(context) {
8
+ const [resource, action, child] = context.path;
9
+ if (resource !== 'agent-config')
10
+ return false;
11
+ if (!action && context.method === 'GET') {
12
+ context.json(200, { configs: listAgentConfigs(context.agents) });
13
+ return true;
14
+ }
15
+ if (child || context.method !== 'POST')
16
+ return false;
17
+ const body = await context.body();
18
+ const id = agentId(body);
19
+ if (!id) {
20
+ context.json(400, { error: 'agent_id is required' });
21
+ return true;
22
+ }
23
+ const agent = context.agents.getAgent(id);
24
+ if (!agent) {
25
+ context.json(404, { error: `Agent not found: ${id}` });
26
+ return true;
27
+ }
28
+ if (action === 'get') {
29
+ context.json(200, { config: configFromAgent(agent) });
30
+ return true;
31
+ }
32
+ if (action === 'update') {
33
+ const config = isObject(body.config) ? body.config : {};
34
+ context.json(200, { ok: true, config: updateAgentConfig(context.agents, id, config).config });
35
+ return true;
36
+ }
37
+ if (action === 'import-native') {
38
+ const result = importAgentConfig(context.agents, id, {
39
+ ...(context.nativeHomeDir ? { homeDir: context.nativeHomeDir } : {}),
40
+ replace: body.replace === true,
41
+ });
42
+ context.json(200, { ok: true, ...result });
43
+ return true;
44
+ }
45
+ return false;
46
+ }
@@ -1,6 +1,8 @@
1
1
  import http from 'node:http';
2
2
  import { Agents } from '../agents.js';
3
3
  import { asAgentError } from '../errors.js';
4
+ import { routeAgentConfig } from './config-routes.js';
5
+ import { routeAgentBundles } from './bundle-routes.js';
4
6
  import { routeAgents } from './agent-routes.js';
5
7
  import { guardRequest, readJson, sendJson } from './request.js';
6
8
  import { routeSessions } from './session-routes.js';
@@ -44,10 +46,12 @@ export function createRestServer(options = {}) {
44
46
  path,
45
47
  url,
46
48
  skillRoots: options.skillRoots || {},
49
+ ...(options.nativeHomeDir ? { nativeHomeDir: options.nativeHomeDir } : {}),
50
+ ...(options.bundlePort ? { bundlePort: options.bundlePort } : {}),
47
51
  body: () => readJson(request, bodyLimit),
48
52
  json: (status, value) => sendJson(response, status, value),
49
53
  };
50
- if (await routeSkills(context))
54
+ if (await routeAgentBundles(context) || await routeAgentConfig(context) || await routeSkills(context))
51
55
  return;
52
56
  if (path.shift() !== 'v1') {
53
57
  sendJson(response, 404, { error: { code: 'not_found', message: 'Route not found.' } });
@@ -3,11 +3,14 @@ import type { AddressInfo } from 'node:net';
3
3
  import type { Agents } from './agents.js';
4
4
  import type { AgentsOptions } from './types.js';
5
5
  import type { SkillRootOptions } from './skills/roots.js';
6
+ import type { AgentBundlePort } from './bundles/types.js';
6
7
  export interface RestServerOptions extends AgentsOptions {
7
8
  agents?: Agents;
8
9
  token?: string;
9
10
  bodyLimitBytes?: number;
10
11
  skillRoots?: SkillRootOptions;
12
+ nativeHomeDir?: string;
13
+ bundlePort?: AgentBundlePort;
11
14
  }
12
15
  export interface RestServer {
13
16
  agents: Agents;
@@ -21,6 +24,8 @@ export interface RouteContext {
21
24
  path: string[];
22
25
  url: URL;
23
26
  skillRoots: SkillRootOptions;
27
+ nativeHomeDir?: string;
28
+ bundlePort?: AgentBundlePort;
24
29
  body(): Promise<Record<string, unknown>>;
25
30
  json(status: number, value: unknown): void;
26
31
  }
package/dist/index.d.ts CHANGED
@@ -8,3 +8,15 @@ export { buildCanonicalSkillRoots, classifySkillRoot } from './skills/roots.js';
8
8
  export type { SkillRootClassification, SkillRootOptions } from './skills/roots.js';
9
9
  export { parseSkillFrontmatter, scanInstalledSkills } from './skills/scanner.js';
10
10
  export type { InstalledSkill, SkillScanOptions } from './skills/scanner.js';
11
+ export { normalizeAgentConfig, stableConfigId } from './config/schema.js';
12
+ export { configFromAgent, listAgentConfigs, updateAgentConfig } from './config/store.js';
13
+ export { importAgentConfig } from './config/import.js';
14
+ export { importNativeConfig } from './config/native.js';
15
+ export type * from './config/types.js';
16
+ export { createAgentBundle } from './bundles/create.js';
17
+ export type { CreateBundleOptions } from './bundles/create.js';
18
+ export { installAgentBundle } from './bundles/install.js';
19
+ export { buildBundleGraph } from './bundles/graph.js';
20
+ export { validateAgentBundle } from './bundles/validate.js';
21
+ export { BUNDLE_KIND, BUNDLE_SCHEMA_VERSION } from './bundles/util.js';
22
+ export type * from './bundles/types.js';
package/dist/index.js CHANGED
@@ -5,3 +5,12 @@ export { definitionHash, normalizeDefinition, patchDefinition } from './definiti
5
5
  export { messageText, normalizeMessage } from './messages.js';
6
6
  export { buildCanonicalSkillRoots, classifySkillRoot } from './skills/roots.js';
7
7
  export { parseSkillFrontmatter, scanInstalledSkills } from './skills/scanner.js';
8
+ export { normalizeAgentConfig, stableConfigId } from './config/schema.js';
9
+ export { configFromAgent, listAgentConfigs, updateAgentConfig } from './config/store.js';
10
+ export { importAgentConfig } from './config/import.js';
11
+ export { importNativeConfig } from './config/native.js';
12
+ export { createAgentBundle } from './bundles/create.js';
13
+ export { installAgentBundle } from './bundles/install.js';
14
+ export { buildBundleGraph } from './bundles/graph.js';
15
+ export { validateAgentBundle } from './bundles/validate.js';
16
+ export { BUNDLE_KIND, BUNDLE_SCHEMA_VERSION } from './bundles/util.js';
@@ -16,7 +16,7 @@ export function createMcpServer(options = {}) {
16
16
  return {
17
17
  protocolVersion: message.params?.protocolVersion || '2024-11-05',
18
18
  capabilities: { tools: { listChanged: false } },
19
- serverInfo: { name: 'amalgm-agents', version: '0.1.1' },
19
+ serverInfo: { name: 'amalgm-agents', version: '0.1.3' },
20
20
  };
21
21
  }
22
22
  if (message.method === 'ping')
@@ -18,9 +18,9 @@ that domain.
18
18
  | credential adapter | `authRef` resolver inside drivers |
19
19
  | Supabase Chat session creation | Chat/cloud adapter observing Agents events |
20
20
 
21
- Agent bundles currently mix agents, apps, automations, and tool bindings. That
22
- cross-product packaging concern should remain outside this repository. A bundle
23
- installer may call each product's public apply method.
21
+ Agent bundle-v2 graph and agent install laws live here. Engine supplies the
22
+ `AgentBundlePort` that exports and installs apps, automations, and tools through
23
+ their public SDKs; neither side imports another product's store.
24
24
 
25
25
  ## Required driver adapter
26
26
 
package/docs/REST.md CHANGED
@@ -10,6 +10,31 @@ token receives the kernel's canonical 401 from `@amalgm/core/transport`,
10
10
  compared in constant time. `/healthz` is a minimal unauthenticated liveness
11
11
  response. JSON bodies default to a 512 KB limit.
12
12
 
13
+ ## Agent configuration
14
+
15
+ | Method | Path | Result |
16
+ |---|---|---|
17
+ | `GET` | `/agent-config` | List normalized configs from live agent revisions |
18
+ | `POST` | `/agent-config/get` | Read `{ agent_id }` |
19
+ | `POST` | `/agent-config/update` | Atomically apply `{ agent_id, config }` |
20
+ | `POST` | `/agent-config/import-native` | Import safe native fields for `{ agent_id, replace? }` |
21
+
22
+ Set `nativeHomeDir` when embedding the server or `--native-home`/
23
+ `AMALGM_NATIVE_HOME` with the REST binary. Native import intentionally ignores
24
+ auth files, secrets, and MCP server configuration.
25
+
26
+ ## Agent bundles
27
+
28
+ | Method | Path | Result |
29
+ |---|---|---|
30
+ | `GET` | `/agent-bundles` | Read bundle-v2 capability and available agent heads |
31
+ | `POST` | `/agent-bundles/preview` | Export requested agent/app/automation/tool heads |
32
+ | `POST` | `/agent-bundles/install` | Validate and install `{ bundle, auth_ref? }` |
33
+
34
+ Embedding hosts inject `bundlePort` for resources owned by Apps, Automations,
35
+ and Toolbox. Agent-only bundles need no port. Bundles with external resources
36
+ fail explicitly when the owner port is absent.
37
+
13
38
  ## Agents
14
39
 
15
40
  | Method | Path | Result |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/agents",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Local-first agent definitions, immutable revisions, and durable sessions.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,