@amalgm/agents 0.2.0 → 0.2.2
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.
- package/PURPOSE.md +3 -0
- package/README.md +2 -1
- package/docs/ENGINE_INTEGRATION.md +9 -26
- package/docs/SHELL_INTEGRATION.md +29 -0
- package/package.json +4 -4
- package/skills/amalgm-agents/SKILL.md +233 -13
package/PURPOSE.md
CHANGED
|
@@ -27,6 +27,9 @@ hosts supply explicit ports for resources owned by other products.
|
|
|
27
27
|
|
|
28
28
|
## Axioms
|
|
29
29
|
|
|
30
|
+
Managed releases bind to one exact published Core version and build from the
|
|
31
|
+
locked dependency graph on the supported Node toolchain.
|
|
32
|
+
|
|
30
33
|
1. A user can create, list, inspect, edit, and delete agents.
|
|
31
34
|
2. A user or another agent can resolve an available agent to one exact
|
|
32
35
|
immutable revision without starting a conversation.
|
package/README.md
CHANGED
|
@@ -115,7 +115,8 @@ The CLI reads the same SQLite registry as the SDK.
|
|
|
115
115
|
- [MCP server](./docs/MCP.md)
|
|
116
116
|
- [Persistence](./docs/DATA_MODEL.md)
|
|
117
117
|
- [Security model](./docs/SECURITY.md)
|
|
118
|
-
- [
|
|
118
|
+
- [Shell integration](./docs/SHELL_INTEGRATION.md)
|
|
119
|
+
- [Historical Engine integration](./docs/ENGINE_INTEGRATION.md)
|
|
119
120
|
|
|
120
121
|
## Development
|
|
121
122
|
|
|
@@ -1,29 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Historical Engine integration
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Amalgm Engine is deprecated. Its former agent registry, configuration, talk,
|
|
4
|
+
REST, and MCP implementations are reference evidence for migrations and
|
|
5
|
+
parity only. They are not active execution or persistence authorities and are
|
|
6
|
+
never a fallback beside Shell.
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
Agents now owns identities, installations, immutable revisions, and portable
|
|
9
|
+
bundles. Chat owns conversations and turns. Shell resolves exact Agents
|
|
10
|
+
revisions into prepared Chat executions through the public packages.
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
revision id to `agents.getAgentRevision(agentId, revisionId)`, then project that
|
|
10
|
-
immutable definition into Chat's execution contract. The resolver never writes
|
|
11
|
-
either product's store.
|
|
12
|
-
|
|
13
|
-
Chat owns the conversation, accepted prompt, provider session, ordered durable
|
|
14
|
-
updates, transcript, usage, reconnect, and interrupt lifecycle. Agents owns the
|
|
15
|
-
identity and revision selected by that execution. Neither product imports the
|
|
16
|
-
other's database.
|
|
17
|
-
|
|
18
|
-
Tool and action ids are resolved through `@amalgm/tools`; `authRef` is resolved
|
|
19
|
-
through the credential boundary. The resulting concrete values are frozen by
|
|
20
|
-
Chat before it accepts a turn.
|
|
21
|
-
|
|
22
|
-
## Integration checks
|
|
23
|
-
|
|
24
|
-
- create, update, list, delete, and exact revision resolution use `Agents`;
|
|
25
|
-
- editing an agent never changes a Chat execution that already names a revision;
|
|
26
|
-
- Toolbox and credential references are authorized during preparation;
|
|
27
|
-
- provider restart, reconnect, and interrupt are handled only by Chat; and
|
|
28
|
-
- agent-to-agent interaction creates or joins a Chat conversation through the
|
|
29
|
-
same prepared-execution protocol as the UI.
|
|
12
|
+
See [SHELL_INTEGRATION.md](./SHELL_INTEGRATION.md) for the active boundary.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Shell integration
|
|
2
|
+
|
|
3
|
+
Shell is the composition root. Agents supplies the canonical registry and Chat
|
|
4
|
+
supplies the canonical conversation runtime.
|
|
5
|
+
|
|
6
|
+
## Resolver boundary
|
|
7
|
+
|
|
8
|
+
Shell gives Chat a resolver with one job: map an installed agent id and optional
|
|
9
|
+
revision id to `agents.getAgentRevision(agentId, revisionId)`, then project that
|
|
10
|
+
immutable definition into Chat's execution contract. The resolver never writes
|
|
11
|
+
either product's store.
|
|
12
|
+
|
|
13
|
+
Chat owns the conversation, accepted prompt, provider session, ordered durable
|
|
14
|
+
updates, transcript, usage, reconnect, and interrupt lifecycle. Agents owns the
|
|
15
|
+
identity and revision selected by that execution. Neither product imports the
|
|
16
|
+
other's database.
|
|
17
|
+
|
|
18
|
+
Tool and action ids are resolved through `@amalgm/tools`; `authRef` is resolved
|
|
19
|
+
through the credential boundary. The resulting concrete values are frozen by
|
|
20
|
+
Chat before it accepts a turn.
|
|
21
|
+
|
|
22
|
+
## Integration checks
|
|
23
|
+
|
|
24
|
+
- create, update, list, delete, and exact revision resolution use `Agents`;
|
|
25
|
+
- editing an agent never changes a Chat execution that already names a revision;
|
|
26
|
+
- Toolbox and credential references are authorized during preparation;
|
|
27
|
+
- provider restart, reconnect, and interrupt are handled only by Chat; and
|
|
28
|
+
- agent-to-agent interaction creates or joins a Chat conversation through the
|
|
29
|
+
same prepared-execution protocol as the UI.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/agents",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Local-first agent identities, installations, and immutable revisions.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"README.md"
|
|
43
43
|
],
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "
|
|
45
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsx scripts/mark-executables.ts",
|
|
46
46
|
"check": "tsx scripts/check-tree.ts && tsc -p tsconfig.json && tsc -p tsconfig.test.json",
|
|
47
47
|
"test": "tsx --test --test-concurrency=1 --test-timeout=30000 test/*.test.ts",
|
|
48
48
|
"verify": "npm run check && npm run build && npm test",
|
|
49
49
|
"prepack": "npm run build"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
|
-
"node": ">=
|
|
52
|
+
"node": ">=24"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@amalgm/core": "
|
|
55
|
+
"@amalgm/core": "0.4.7",
|
|
56
56
|
"@types/better-sqlite3": "^7.6.13",
|
|
57
57
|
"@types/node": "^20.19.43",
|
|
58
58
|
"better-sqlite3": "^12.6.2"
|
|
@@ -1,21 +1,241 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: amalgm-agents
|
|
3
|
-
description:
|
|
3
|
+
description: Manage persistent Amalgm agent identities and their immutable revisions with the @amalgm/agents SDK, CLI, REST API, or MCP tools. Use when you need to define an agent (its harness, model, instructions, tools, skills, and workspace), list or inspect existing agents, edit or delete one, or resolve the exact revision to run through Chat.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Agents
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
`amalgm-agents` CLI command.
|
|
8
|
+
Agents is the registry for durable agent definitions on the Amalgm platform. It stores three things: **identities** (a stable id like `reviewer` that survives renames and edits), **installations** (the agent identities available on the local machine), and **immutable revisions** (append-only snapshots of every definition an identity has ever had). You define an agent once — its harness, model, instructions, tools, skills, and workspace — and every surface on the machine (SDK, CLI, REST, MCP, and the Amalgm apps) reads and writes the same local SQLite registry. Agents does not run conversations: it resolves an exact revision, and Chat executes it.
|
|
10
9
|
|
|
11
|
-
##
|
|
10
|
+
## Installation
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
2. Create from a complete definition.
|
|
15
|
-
3. Update only the fields the user requested.
|
|
16
|
-
4. Delete only when the user explicitly asks.
|
|
17
|
-
5. Keep tool, skill, file, credential, and subagent values as references; do
|
|
18
|
-
not copy those products into the agent definition.
|
|
12
|
+
Install the package from npm. It requires Node 20 or later.
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
```bash
|
|
15
|
+
npm install @amalgm/agents
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The install also provides three executables: `amalgm-agents` (CLI), `amalgm-agents-mcp` (stdio MCP server), and `amalgm-agents-rest` (loopback REST server). Run them with `npx` or add the package's `.bin` directory to your `PATH`.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx amalgm-agents --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
Create an agent, list what exists, and resolve the revision you would hand to Chat. The `Agents` class is synchronous and local-first — every mutation is written to disk before the call returns.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { Agents } from '@amalgm/agents';
|
|
30
|
+
|
|
31
|
+
const agents = new Agents({ stateDir: './state' });
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// Define and create an agent. `id`, `name`, and `driver` are required;
|
|
35
|
+
// everything else has a sensible empty default.
|
|
36
|
+
agents.createAgent({
|
|
37
|
+
id: 'reviewer',
|
|
38
|
+
name: 'Reviewer',
|
|
39
|
+
description: 'Reviews implementation work',
|
|
40
|
+
driver: 'codex',
|
|
41
|
+
instructions: 'Find correctness risks before style issues.',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// List every available (non-deleted) agent.
|
|
45
|
+
for (const agent of agents.listAgents()) {
|
|
46
|
+
console.log(agent.id, agent.definition.name, `rev ${agent.currentRevision}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Inspect one agent's current definition.
|
|
50
|
+
const record = agents.getAgent('reviewer');
|
|
51
|
+
console.log(record?.definition.instructions);
|
|
52
|
+
|
|
53
|
+
// Resolve the exact immutable revision a host such as Chat would execute.
|
|
54
|
+
const revision = agents.getAgentRevision('reviewer');
|
|
55
|
+
console.log(revision.id, revision.number, revision.hash);
|
|
56
|
+
} finally {
|
|
57
|
+
agents.close();
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
By default the registry lives at `~/.amalgm/users/local/agents/agents.db`. Pass `stateDir` to keep state somewhere else (useful for tests and embedded hosts), or `databasePath` to point at a database file directly. Setting the `AMALGAM_AGENTS_DIR` environment variable overrides the default location without code changes.
|
|
62
|
+
|
|
63
|
+
## Core concepts
|
|
64
|
+
|
|
65
|
+
Agents separates three ideas that most agent frameworks blur together. Keeping them distinct is what makes agent executions reproducible.
|
|
66
|
+
|
|
67
|
+
### Identity
|
|
68
|
+
|
|
69
|
+
An identity is the stable machine name of an agent: 1–128 lowercase letters, numbers, dots, underscores, or hyphens. Renaming the human-readable `name` never changes the `id`, and other products reference agents by this id. Deleting an agent tombstones the identity rather than erasing it — the id stays reserved so that recorded history can never silently point at a different, later agent.
|
|
70
|
+
|
|
71
|
+
### Revision
|
|
72
|
+
|
|
73
|
+
Every content change to a definition appends an immutable revision: a numbered, hashed snapshot of the complete normalized definition. Revisions are immutable for one reason — Chat records the exact revision used by each execution, so a conversation transcript always names precisely the instructions, model, and tool references that produced it. Editing an agent today can never rewrite what an execution meant yesterday.
|
|
74
|
+
|
|
75
|
+
Two consequences follow from the append-only design:
|
|
76
|
+
|
|
77
|
+
- **Idempotent edits.** Submitting a patch that produces identical content keeps the current revision; nothing is appended.
|
|
78
|
+
- **No revision reuse.** Reverting to content an earlier revision had still creates a *new* revision number, so the timeline of what the definition was, and when, stays complete.
|
|
79
|
+
|
|
80
|
+
### Installation
|
|
81
|
+
|
|
82
|
+
An installation is one agent identity available on the local machine, together with its home descriptor. The registry is per-machine and local-first: `listAgents()` answers "which agents can this machine run right now". Portable bundles let you move an agent (with its subagent, skill, and tool graph) between machines; installing a bundle creates fresh local identities rather than overwriting existing ones.
|
|
83
|
+
|
|
84
|
+
## Agent definitions
|
|
85
|
+
|
|
86
|
+
A definition is complete, portable execution configuration. It holds *references* to things owned by other products — tools, skills, files, credentials, subagents — never copies of them and never secrets.
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"id": "reviewer",
|
|
91
|
+
"name": "Reviewer",
|
|
92
|
+
"description": "Reviews implementation work",
|
|
93
|
+
"instructions": "Find correctness risks before style issues.",
|
|
94
|
+
"driver": { "id": "codex", "config": { "sandbox": "workspace-write" } },
|
|
95
|
+
"model": { "id": "openai/gpt-5.5", "settings": { "effort": "high" } },
|
|
96
|
+
"authRef": "openai-primary",
|
|
97
|
+
"resources": {
|
|
98
|
+
"files": ["AGENTS.md"],
|
|
99
|
+
"skills": ["code-review"],
|
|
100
|
+
"subagents": ["security-reviewer"]
|
|
101
|
+
},
|
|
102
|
+
"toolbox": {
|
|
103
|
+
"toolIds": ["git"],
|
|
104
|
+
"actionIds": ["github.pull_request_get"]
|
|
105
|
+
},
|
|
106
|
+
"workspace": { "cwd": "/projects/product" },
|
|
107
|
+
"metadata": { "icon": "search" }
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Field notes:
|
|
112
|
+
|
|
113
|
+
- `driver` names the harness that will run the agent. As shorthand you may pass a plain string (`"driver": "codex"` becomes `{ "id": "codex", "config": {} }`). `driver.config` is non-secret harness configuration interpreted at execution time.
|
|
114
|
+
- `model` and `authRef` are nullable. `authRef` names a credential that the host resolves when the agent runs; the secret itself never enters the definition.
|
|
115
|
+
- `resources` and `toolbox` are lists of opaque ids resolved by the host. Arrays are de-duplicated and order-normalized.
|
|
116
|
+
- `metadata` is free-form JSON for your own annotations (icons, tags, owners).
|
|
117
|
+
|
|
118
|
+
## SDK reference
|
|
119
|
+
|
|
120
|
+
`Agents` is the single behavior boundary — the CLI, REST, and MCP surfaces are thin adapters over these same methods, so behavior is identical everywhere.
|
|
121
|
+
|
|
122
|
+
| Method | Behavior |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `createAgent(definition)` | Create an identity and immutable revision 1 |
|
|
125
|
+
| `updateAgent(id, patch)` | Merge a patch; append a revision only when content changes |
|
|
126
|
+
| `deleteAgent(id)` | Tombstone the identity, retaining all revisions |
|
|
127
|
+
| `getAgent(id, includeDeleted?)` | Read the current record, or `null` if absent |
|
|
128
|
+
| `listAgents(includeDeleted?)` | List available agents (tombstones excluded by default) |
|
|
129
|
+
| `getAgentRevision(agentId, revisionId?)` | Resolve the current or one exact immutable revision |
|
|
130
|
+
| `health()` | Liveness summary for the open registry |
|
|
131
|
+
| `close()` | Release the underlying database |
|
|
132
|
+
|
|
133
|
+
### Patching
|
|
134
|
+
|
|
135
|
+
Patches are partial definitions with predictable merge rules: top-level fields replace their prior values, while the known nested objects — `driver`, `resources`, `toolbox`, and `workspace` — merge field by field. That means a patch touching only `resources.skills` preserves `resources.files` and `resources.subagents`. Set a nullable field to `null` to clear it.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
const updated = agents.updateAgent('reviewer', {
|
|
139
|
+
instructions: 'Review for correctness first, style second.',
|
|
140
|
+
resources: { skills: ['code-review', 'security-review'] },
|
|
141
|
+
model: null, // clear the pinned model; the host default applies
|
|
142
|
+
});
|
|
143
|
+
console.log(updated.currentRevision); // bumped, because content changed
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Errors
|
|
147
|
+
|
|
148
|
+
Expected failures throw `AgentError` with a stable `code` — `invalid_input`, `not_found`, `conflict`, `deleted`, or `too_large` — plus a matching HTTP `status` used by the REST surface. Catch and branch on the code rather than the message.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { Agents, AgentError } from '@amalgm/agents';
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
agents.updateAgent('missing-id', { name: 'Nope' });
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof AgentError && error.code === 'not_found') {
|
|
157
|
+
console.log('No such agent.');
|
|
158
|
+
} else {
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## CLI
|
|
165
|
+
|
|
166
|
+
The `amalgm-agents` CLI shares the SDK database — there is no separate CLI registry. It prints JSON to stdout and errors to stderr, so it composes cleanly with `jq` and scripts.
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
amalgm-agents agent list [--include-deleted]
|
|
170
|
+
amalgm-agents agent show <id> [--include-deleted]
|
|
171
|
+
amalgm-agents agent create <definition.json>
|
|
172
|
+
amalgm-agents agent update <id> <patch.json>
|
|
173
|
+
amalgm-agents agent delete <id>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`create` and `update` read a JSON file — a complete definition and a partial patch respectively, using exactly the shapes shown above. Global options are `--state-dir <path>` (choose the registry location) and `--help`.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Create from a file, then confirm the revision advanced after an edit.
|
|
180
|
+
amalgm-agents agent create reviewer.json
|
|
181
|
+
echo '{"instructions": "Be terse."}' > patch.json
|
|
182
|
+
amalgm-agents agent update reviewer patch.json
|
|
183
|
+
amalgm-agents agent show reviewer | jq .currentRevision
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## REST API
|
|
187
|
+
|
|
188
|
+
For non-Node hosts, run the loopback REST server or embed it with `createRestServer` from `@amalgm/agents/http`.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
amalgm-agents-rest --host 127.0.0.1 --port 4317 --token "$TOKEN"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
When a token is configured, authenticate with `Authorization: Bearer <token>`; `/healthz` stays unauthenticated. JSON bodies are limited to 512 KB by default.
|
|
195
|
+
|
|
196
|
+
| Method | Path | Result |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `GET` | `/v1/agents` | List available agents |
|
|
199
|
+
| `POST` | `/v1/agents` | Create from a complete definition |
|
|
200
|
+
| `GET` | `/v1/agents/:id` | Read the current definition |
|
|
201
|
+
| `PATCH` | `/v1/agents/:id` | Apply a definition patch |
|
|
202
|
+
| `DELETE` | `/v1/agents/:id` | Tombstone an identity |
|
|
203
|
+
|
|
204
|
+
Add `?include_deleted=true` to list or get requests to include tombstones. There are no conversation routes — execution belongs to Chat.
|
|
205
|
+
|
|
206
|
+
## MCP tools
|
|
207
|
+
|
|
208
|
+
`amalgm-agents-mcp` runs the registry as a stdio MCP server, so any MCP-capable assistant can manage agents. `createMcpServer` from `@amalgm/agents/mcp` embeds the same server in your own host.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
amalgm-agents-mcp --state-dir ./state
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
It exposes five tools, each a direct projection of an SDK method:
|
|
215
|
+
|
|
216
|
+
| Tool | Arguments | Behavior |
|
|
217
|
+
|---|---|---|
|
|
218
|
+
| `agents_list` | `include_deleted?` | List persistent agent definitions |
|
|
219
|
+
| `agents_get` | `agent_id`, `include_deleted?` | Read one agent and its current revision |
|
|
220
|
+
| `agents_create` | `definition` | Create from a complete definition |
|
|
221
|
+
| `agents_update` | `agent_id`, `patch` | Patch; content changes append a revision |
|
|
222
|
+
| `agents_delete` | `agent_id` | Tombstone an identity, retaining revisions |
|
|
223
|
+
|
|
224
|
+
When working through these tools, list or inspect before changing an existing agent, patch only the fields being changed, and keep tool, skill, file, credential, and subagent values as references rather than inlining their content.
|
|
225
|
+
|
|
226
|
+
## Running agents with Chat
|
|
227
|
+
|
|
228
|
+
Agents deliberately contains no conversation, execution, transcript, interrupt, or reconnect operation. To run an agent, resolve its identity here and hand the result to Chat:
|
|
229
|
+
|
|
230
|
+
1. Select an installed agent identity (from `listAgents` or a known id).
|
|
231
|
+
2. Call `getAgentRevision(agentId, revisionId?)` — you get the current revision, or one exact historical revision if you pass its id.
|
|
232
|
+
3. Project that immutable definition into Chat's execution contract. Chat resolves the `toolbox`, `resources`, and `authRef` references into concrete values, freezes them, and then accepts the turn.
|
|
233
|
+
4. Chat records which revision it executed, so the conversation remains reproducible even after the agent is edited or deleted.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
const revision = agents.getAgentRevision('reviewer');
|
|
237
|
+
// revision.definition is the frozen, normalized definition:
|
|
238
|
+
// pass it to Chat to start or continue a conversation as this agent.
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The division of labor is strict by design: Agents answers "what is this agent, exactly?", and Chat owns everything about running it — the conversation, turns, streams, usage, interrupts, and reconnects. Editing an agent never changes a Chat execution that already names a revision.
|