@amalgm/agents 0.1.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.
- package/PURPOSE.md +45 -0
- package/README.md +110 -0
- package/dist/agent-store.d.ts +17 -0
- package/dist/agent-store.js +111 -0
- package/dist/agents.d.ts +39 -0
- package/dist/agents.js +129 -0
- package/dist/bin/agents.d.ts +2 -0
- package/dist/bin/agents.js +5 -0
- package/dist/bin/fatal.d.ts +1 -0
- package/dist/bin/fatal.js +5 -0
- package/dist/bin/mcp.d.ts +2 -0
- package/dist/bin/mcp.js +19 -0
- package/dist/bin/rest.d.ts +2 -0
- package/dist/bin/rest.js +21 -0
- package/dist/cli/agent-commands.d.ts +3 -0
- package/dist/cli/agent-commands.js +23 -0
- package/dist/cli/args.d.ts +7 -0
- package/dist/cli/args.js +31 -0
- package/dist/cli/files.d.ts +2 -0
- package/dist/cli/files.js +21 -0
- package/dist/cli/help.d.ts +1 -0
- package/dist/cli/help.js +24 -0
- package/dist/cli/open.d.ts +3 -0
- package/dist/cli/open.js +12 -0
- package/dist/cli/run.d.ts +1 -0
- package/dist/cli/run.js +38 -0
- package/dist/cli/session-commands.d.ts +4 -0
- package/dist/cli/session-commands.js +54 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +2 -0
- package/dist/database.d.ts +9 -0
- package/dist/database.js +35 -0
- package/dist/definition.d.ts +4 -0
- package/dist/definition.js +93 -0
- package/dist/drivers.d.ts +4 -0
- package/dist/drivers.js +25 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +28 -0
- package/dist/event-store.d.ts +13 -0
- package/dist/event-store.js +50 -0
- package/dist/http/agent-routes.d.ts +2 -0
- package/dist/http/agent-routes.js +40 -0
- package/dist/http/request.d.ts +5 -0
- package/dist/http/request.js +40 -0
- package/dist/http/server.d.ts +2 -0
- package/dist/http/server.js +82 -0
- package/dist/http/session-routes.d.ts +2 -0
- package/dist/http/session-routes.js +67 -0
- package/dist/http/stream.d.ts +3 -0
- package/dist/http/stream.js +30 -0
- package/dist/http-types.d.ts +23 -0
- package/dist/http-types.js +1 -0
- package/dist/http.d.ts +2 -0
- package/dist/http.js +1 -0
- package/dist/ids.d.ts +5 -0
- package/dist/ids.js +32 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/json.d.ts +7 -0
- package/dist/json.js +41 -0
- package/dist/mcp/agent-tools.d.ts +3 -0
- package/dist/mcp/agent-tools.js +51 -0
- package/dist/mcp/helpers.d.ts +5 -0
- package/dist/mcp/helpers.js +27 -0
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mcp/server.js +73 -0
- package/dist/mcp/session-tools.d.ts +3 -0
- package/dist/mcp/session-tools.js +81 -0
- package/dist/mcp/tools.d.ts +3 -0
- package/dist/mcp/tools.js +5 -0
- package/dist/mcp/types.d.ts +18 -0
- package/dist/mcp/types.js +1 -0
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +2 -0
- package/dist/messages.d.ts +3 -0
- package/dist/messages.js +56 -0
- package/dist/rows.d.ts +7 -0
- package/dist/rows.js +65 -0
- package/dist/runtime.d.ts +29 -0
- package/dist/runtime.js +176 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.js +127 -0
- package/dist/session-store.d.ts +15 -0
- package/dist/session-store.js +83 -0
- package/dist/turn-store.d.ts +31 -0
- package/dist/turn-store.js +164 -0
- package/dist/types.d.ts +176 -0
- package/dist/types.js +1 -0
- package/docs/ARCHITECTURE.md +73 -0
- package/docs/CLI.md +61 -0
- package/docs/DATA_MODEL.md +56 -0
- package/docs/DEFINITIONS.md +65 -0
- package/docs/DRIVERS.md +72 -0
- package/docs/ENGINE_INTEGRATION.md +74 -0
- package/docs/MCP.md +24 -0
- package/docs/REST.md +78 -0
- package/docs/SDK.md +72 -0
- package/docs/SECURITY.md +38 -0
- package/examples/basic.ts +31 -0
- package/examples/reviewer.json +25 -0
- package/package.json +64 -0
- package/skills/amalgm-agents/SKILL.md +30 -0
- package/skills/amalgm-agents/agents/openai.yaml +4 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonValue[];
|
|
3
|
+
export type JsonObject = {
|
|
4
|
+
[key: string]: JsonValue;
|
|
5
|
+
};
|
|
6
|
+
export interface AgentModel {
|
|
7
|
+
id: string;
|
|
8
|
+
settings: JsonObject;
|
|
9
|
+
}
|
|
10
|
+
export interface AgentDriverRef {
|
|
11
|
+
id: string;
|
|
12
|
+
config: JsonObject;
|
|
13
|
+
}
|
|
14
|
+
export interface AgentResources {
|
|
15
|
+
files: string[];
|
|
16
|
+
skills: string[];
|
|
17
|
+
subagents: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface ToolboxLoadoutRef {
|
|
20
|
+
toolIds: string[];
|
|
21
|
+
actionIds: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface AgentWorkspace {
|
|
24
|
+
cwd: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface AgentDefinition {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
instructions: string;
|
|
31
|
+
driver: AgentDriverRef;
|
|
32
|
+
model: AgentModel | null;
|
|
33
|
+
authRef: string | null;
|
|
34
|
+
resources: AgentResources;
|
|
35
|
+
toolbox: ToolboxLoadoutRef;
|
|
36
|
+
workspace: AgentWorkspace;
|
|
37
|
+
metadata: JsonObject;
|
|
38
|
+
}
|
|
39
|
+
export type AgentDefinitionInput = Partial<Omit<AgentDefinition, 'driver' | 'model' | 'resources' | 'toolbox' | 'workspace'>> & {
|
|
40
|
+
driver?: string | Partial<AgentDriverRef>;
|
|
41
|
+
model?: (Omit<AgentModel, 'settings'> & {
|
|
42
|
+
settings?: JsonObject;
|
|
43
|
+
}) | null;
|
|
44
|
+
resources?: Partial<AgentResources>;
|
|
45
|
+
toolbox?: Partial<ToolboxLoadoutRef>;
|
|
46
|
+
workspace?: Partial<AgentWorkspace>;
|
|
47
|
+
};
|
|
48
|
+
export type AgentPatch = Omit<Partial<AgentDefinitionInput>, 'id'>;
|
|
49
|
+
export interface AgentRevision {
|
|
50
|
+
id: string;
|
|
51
|
+
agentId: string;
|
|
52
|
+
number: number;
|
|
53
|
+
hash: string;
|
|
54
|
+
definition: AgentDefinition;
|
|
55
|
+
createdAt: string;
|
|
56
|
+
}
|
|
57
|
+
export interface AgentRecord {
|
|
58
|
+
id: string;
|
|
59
|
+
currentRevisionId: string;
|
|
60
|
+
currentRevision: number;
|
|
61
|
+
definition: AgentDefinition;
|
|
62
|
+
createdAt: string;
|
|
63
|
+
updatedAt: string;
|
|
64
|
+
deletedAt: string | null;
|
|
65
|
+
}
|
|
66
|
+
export type SessionStatus = 'active' | 'archived';
|
|
67
|
+
export type TurnStatus = 'queued' | 'running' | 'cancelling' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
|
|
68
|
+
export interface TextPart {
|
|
69
|
+
type: 'text';
|
|
70
|
+
text: string;
|
|
71
|
+
}
|
|
72
|
+
export interface ReferencePart {
|
|
73
|
+
type: 'reference';
|
|
74
|
+
uri: string;
|
|
75
|
+
name: string | null;
|
|
76
|
+
mediaType: string | null;
|
|
77
|
+
}
|
|
78
|
+
export interface DataPart {
|
|
79
|
+
type: 'data';
|
|
80
|
+
name: string;
|
|
81
|
+
data: JsonValue;
|
|
82
|
+
}
|
|
83
|
+
export type AgentPart = TextPart | ReferencePart | DataPart;
|
|
84
|
+
export type MessageRole = 'user' | 'assistant' | 'system';
|
|
85
|
+
export interface AgentMessage {
|
|
86
|
+
role: MessageRole;
|
|
87
|
+
parts: AgentPart[];
|
|
88
|
+
}
|
|
89
|
+
export interface AgentSession {
|
|
90
|
+
id: string;
|
|
91
|
+
agentId: string;
|
|
92
|
+
agentRevisionId: string;
|
|
93
|
+
agentRevision: number;
|
|
94
|
+
status: SessionStatus;
|
|
95
|
+
driverSessionId: string | null;
|
|
96
|
+
metadata: JsonObject;
|
|
97
|
+
createdAt: string;
|
|
98
|
+
updatedAt: string;
|
|
99
|
+
archivedAt: string | null;
|
|
100
|
+
}
|
|
101
|
+
export interface AgentTurn {
|
|
102
|
+
id: string;
|
|
103
|
+
sessionId: string;
|
|
104
|
+
idempotencyKey: string;
|
|
105
|
+
status: TurnStatus;
|
|
106
|
+
input: AgentMessage;
|
|
107
|
+
result: JsonObject | null;
|
|
108
|
+
error: AgentFailure | null;
|
|
109
|
+
createdAt: string;
|
|
110
|
+
startedAt: string | null;
|
|
111
|
+
completedAt: string | null;
|
|
112
|
+
}
|
|
113
|
+
export interface AgentFailure {
|
|
114
|
+
code: string;
|
|
115
|
+
message: string;
|
|
116
|
+
details: JsonObject;
|
|
117
|
+
}
|
|
118
|
+
export interface SessionEvent {
|
|
119
|
+
id: string;
|
|
120
|
+
sessionId: string;
|
|
121
|
+
turnId: string | null;
|
|
122
|
+
sequence: number;
|
|
123
|
+
type: string;
|
|
124
|
+
data: JsonObject;
|
|
125
|
+
createdAt: string;
|
|
126
|
+
}
|
|
127
|
+
export interface DriverEvent {
|
|
128
|
+
type: string;
|
|
129
|
+
data: JsonObject;
|
|
130
|
+
}
|
|
131
|
+
export interface DriverRunRequest {
|
|
132
|
+
agent: AgentRevision;
|
|
133
|
+
session: AgentSession;
|
|
134
|
+
turn: AgentTurn;
|
|
135
|
+
history: AgentMessage[];
|
|
136
|
+
input: AgentMessage;
|
|
137
|
+
driverSessionId: string | null;
|
|
138
|
+
}
|
|
139
|
+
export interface DriverRunResult {
|
|
140
|
+
message?: AgentMessage;
|
|
141
|
+
driverSessionId?: string;
|
|
142
|
+
metadata?: JsonObject;
|
|
143
|
+
}
|
|
144
|
+
export interface DriverRunContext {
|
|
145
|
+
signal: AbortSignal;
|
|
146
|
+
emit(event: DriverEvent): SessionEvent;
|
|
147
|
+
}
|
|
148
|
+
export interface AgentDriver {
|
|
149
|
+
id: string;
|
|
150
|
+
run(request: DriverRunRequest, context: DriverRunContext): Promise<DriverRunResult | void>;
|
|
151
|
+
}
|
|
152
|
+
export interface AgentsOptions {
|
|
153
|
+
stateDir?: string;
|
|
154
|
+
databasePath?: string;
|
|
155
|
+
drivers?: AgentDriver[];
|
|
156
|
+
maxInputBytes?: number;
|
|
157
|
+
maxEventBytes?: number;
|
|
158
|
+
turnTimeoutMs?: number;
|
|
159
|
+
now?: () => string;
|
|
160
|
+
}
|
|
161
|
+
export interface StartSessionInput {
|
|
162
|
+
agentId: string;
|
|
163
|
+
revisionId?: string;
|
|
164
|
+
sessionId?: string;
|
|
165
|
+
metadata?: JsonObject;
|
|
166
|
+
}
|
|
167
|
+
export interface SendInput {
|
|
168
|
+
message: string | AgentMessage;
|
|
169
|
+
idempotencyKey?: string;
|
|
170
|
+
}
|
|
171
|
+
export interface EnqueuedTurn {
|
|
172
|
+
turn: AgentTurn;
|
|
173
|
+
completion: Promise<AgentTurn>;
|
|
174
|
+
duplicate: boolean;
|
|
175
|
+
}
|
|
176
|
+
export type EventListener = (event: SessionEvent) => void;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Ownership
|
|
4
|
+
|
|
5
|
+
The Agents service owns four facts:
|
|
6
|
+
|
|
7
|
+
1. which agent identities exist;
|
|
8
|
+
2. the immutable revisions of each identity;
|
|
9
|
+
3. which revision each session uses; and
|
|
10
|
+
4. the ordered turns and events inside each session.
|
|
11
|
+
|
|
12
|
+
Everything else crosses an adapter boundary.
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
SDK
|
|
16
|
+
│
|
|
17
|
+
CLI ───┐ │ ┌─── REST + SSE
|
|
18
|
+
├── Agents ───┤
|
|
19
|
+
MCP ───┘ │ └─── Engine adapter
|
|
20
|
+
│
|
|
21
|
+
SQLite ledger
|
|
22
|
+
│
|
|
23
|
+
AgentDriver
|
|
24
|
+
┌──────┼──────┐
|
|
25
|
+
Codex Claude custom
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
CLI, MCP, REST, and Engine never write SQLite directly. `Agents` is the
|
|
29
|
+
public service; the stores below it are implementation details.
|
|
30
|
+
|
|
31
|
+
## Product boundaries
|
|
32
|
+
|
|
33
|
+
| Product | Owns | Agents keeps |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Chat | presentation, titles, participants, read state | no Chat record |
|
|
36
|
+
| Tools | tools, actions, drivers, loadouts | opaque tool/action ids |
|
|
37
|
+
| Skills | skill content and installation | opaque skill ids |
|
|
38
|
+
| Credentials | secret material and authorization | opaque `authRef` |
|
|
39
|
+
| Engine | native harness processes, cloud routing, composition | injected drivers |
|
|
40
|
+
| Agents | definitions, revisions, sessions, turns, events | canonical records |
|
|
41
|
+
|
|
42
|
+
An Agents session is executable history. A Chat conversation is a presentation
|
|
43
|
+
object. A Chat may point to a session; the reverse dependency is forbidden.
|
|
44
|
+
|
|
45
|
+
## Mutation path
|
|
46
|
+
|
|
47
|
+
`createAgent` creates an identity and revision 1 atomically. `updateAgent`
|
|
48
|
+
merges a typed patch into the current definition and compares its canonical
|
|
49
|
+
hash with the current revision. Equal content is idempotent; changed content
|
|
50
|
+
appends a revision and moves the identity's current pointer.
|
|
51
|
+
|
|
52
|
+
Deleting an agent sets `deleted_at`. It does not delete revisions or sessions.
|
|
53
|
+
The id cannot be recreated accidentally.
|
|
54
|
+
|
|
55
|
+
## Execution path
|
|
56
|
+
|
|
57
|
+
1. A caller starts a session; the current revision id is copied onto it.
|
|
58
|
+
2. A turn and its input event commit in one transaction.
|
|
59
|
+
3. The turn becomes `running` before its driver is invoked.
|
|
60
|
+
4. Driver events commit to the ordered event ledger before subscribers see them.
|
|
61
|
+
5. The driver returns a message, opaque native session id, and metadata.
|
|
62
|
+
6. The turn reaches exactly one terminal state.
|
|
63
|
+
|
|
64
|
+
One session serializes turns. Different sessions can execute concurrently.
|
|
65
|
+
|
|
66
|
+
## Why drivers are injected
|
|
67
|
+
|
|
68
|
+
Native agent runtimes have different authentication, installation, process,
|
|
69
|
+
streaming, and resume rules. Baking them into storage would couple every user
|
|
70
|
+
of the SDK to Engine. `AgentDriver` receives a complete immutable revision and
|
|
71
|
+
ordered history, then emits durable events. Engine can reuse its current
|
|
72
|
+
chat-core adapters behind this interface; standalone users can provide their
|
|
73
|
+
own drivers.
|
package/docs/CLI.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# CLI reference
|
|
2
|
+
|
|
3
|
+
The CLI prints JSON to stdout and errors to stderr. It shares the SDK database;
|
|
4
|
+
there is no CLI-specific registry.
|
|
5
|
+
|
|
6
|
+
## Global options
|
|
7
|
+
|
|
8
|
+
| Option | Meaning |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `--state-dir <path>` | Override the Agents state directory |
|
|
11
|
+
| `--drivers <a.js,b.js>` | Load compiled driver modules |
|
|
12
|
+
| `--help` | Print command help |
|
|
13
|
+
|
|
14
|
+
`AMALGM_AGENT_DRIVERS` supplies the same comma-separated driver list.
|
|
15
|
+
|
|
16
|
+
## Agent commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
amalgm-agents agent list [--include-deleted]
|
|
20
|
+
amalgm-agents agent show <id> [--include-deleted]
|
|
21
|
+
amalgm-agents agent create <definition.json>
|
|
22
|
+
amalgm-agents agent update <id> <patch.json>
|
|
23
|
+
amalgm-agents agent delete <id>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`create` requires an unused id. `update` uses the nested PATCH rules in
|
|
27
|
+
[Definitions](./DEFINITIONS.md).
|
|
28
|
+
|
|
29
|
+
## Session commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
amalgm-agents session list [--agent <id>] [--include-archived]
|
|
33
|
+
amalgm-agents session start <agent-id> [--id <id>] [--revision <revision-id>]
|
|
34
|
+
amalgm-agents session show <id>
|
|
35
|
+
amalgm-agents session events <id> [--after <sequence>]
|
|
36
|
+
amalgm-agents session send <id> <message> [--key <idempotency-key>]
|
|
37
|
+
amalgm-agents session cancel <id>
|
|
38
|
+
amalgm-agents session archive <id>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`session show` returns the session, turns, and event ledger together.
|
|
42
|
+
|
|
43
|
+
## Talk shortcut
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
amalgm-agents talk <agent-id> <message> \
|
|
47
|
+
[--session <session-id>] [--key <idempotency-key>] [--background]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Without `--session`, the command starts one. Foreground waits for a terminal
|
|
51
|
+
turn; background returns the accepted turn immediately.
|
|
52
|
+
|
|
53
|
+
## Servers
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
amalgm-agents-rest --host 127.0.0.1 --port 4317 --token "$TOKEN"
|
|
57
|
+
amalgm-agents-mcp --state-dir ./state --drivers ./dist/driver.js
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The REST CLI binds loopback by default. Use a bearer token before deliberately
|
|
61
|
+
binding to any shared interface.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Persistence and recovery
|
|
2
|
+
|
|
3
|
+
The default database is `agents.db` inside the state directory resolved by
|
|
4
|
+
`@amalgm/core/identity`: `AMALGM_AGENTS_DIR` verbatim when set, otherwise
|
|
5
|
+
`$AMALGM_DIR/agents`, otherwise the scoped layout
|
|
6
|
+
`~/.amalgm/users/local/agents`. Set `stateDir` or `databasePath` to relocate
|
|
7
|
+
it directly.
|
|
8
|
+
|
|
9
|
+
## Tables
|
|
10
|
+
|
|
11
|
+
| Table | Role |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `agents` | stable identity, current revision pointer, tombstone |
|
|
14
|
+
| `agent_revisions` | immutable normalized definitions |
|
|
15
|
+
| `sessions` | pinned revision and opaque driver session id |
|
|
16
|
+
| `turns` | accepted input and execution outcome |
|
|
17
|
+
| `session_events` | ordered append-only execution observations |
|
|
18
|
+
|
|
19
|
+
Foreign keys prevent orphan sessions, turns, and events. SQLite WAL mode and a
|
|
20
|
+
five-second busy timeout support multiple read-oriented surfaces while one
|
|
21
|
+
service owns mutations.
|
|
22
|
+
|
|
23
|
+
Repeating the current definition is idempotent. Returning to content used by
|
|
24
|
+
an earlier revision creates a new revision number so the definition timeline
|
|
25
|
+
remains complete and append-only.
|
|
26
|
+
|
|
27
|
+
## Turn states
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
queued → running → completed
|
|
31
|
+
├──→ failed
|
|
32
|
+
├──→ cancelling → cancelled
|
|
33
|
+
└──→ interrupted
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`queued`, `running`, and `cancelling` are non-terminal. At service startup they
|
|
37
|
+
become `interrupted`, because the previous process may have completed external
|
|
38
|
+
side effects before dying. The service never guesses and never automatically
|
|
39
|
+
replays uncertain work.
|
|
40
|
+
|
|
41
|
+
## Idempotency
|
|
42
|
+
|
|
43
|
+
`idempotencyKey` is unique within a session. Repeating a key returns its
|
|
44
|
+
existing turn and does not invoke the driver again. Callers should derive it
|
|
45
|
+
from their durable request identity instead of generating a new value on every
|
|
46
|
+
retry.
|
|
47
|
+
|
|
48
|
+
## Events
|
|
49
|
+
|
|
50
|
+
Every event receives the next integer sequence within its session. SSE first
|
|
51
|
+
reads committed events after the requested sequence, then subscribes to newly
|
|
52
|
+
committed events. Reconnecting with the last SSE id therefore closes the gap
|
|
53
|
+
without polling or losing events.
|
|
54
|
+
|
|
55
|
+
Events are facts, not mutable projections. User interfaces derive streaming
|
|
56
|
+
text, messages, tool activity, and status from the ledger.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Agent definitions
|
|
2
|
+
|
|
3
|
+
An agent definition is complete, portable execution configuration. It contains
|
|
4
|
+
references, never resolved secrets or copies of records owned by other
|
|
5
|
+
products.
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"id": "reviewer",
|
|
10
|
+
"name": "Reviewer",
|
|
11
|
+
"description": "Reviews implementation work",
|
|
12
|
+
"instructions": "Find correctness risks before style issues.",
|
|
13
|
+
"driver": {
|
|
14
|
+
"id": "codex",
|
|
15
|
+
"config": { "sandbox": "workspace-write" }
|
|
16
|
+
},
|
|
17
|
+
"model": {
|
|
18
|
+
"id": "openai/gpt-5.5",
|
|
19
|
+
"settings": { "effort": "high" }
|
|
20
|
+
},
|
|
21
|
+
"authRef": "openai-primary",
|
|
22
|
+
"resources": {
|
|
23
|
+
"files": ["AGENTS.md"],
|
|
24
|
+
"skills": ["code-review"],
|
|
25
|
+
"subagents": ["security-reviewer"]
|
|
26
|
+
},
|
|
27
|
+
"toolbox": {
|
|
28
|
+
"toolIds": ["git"],
|
|
29
|
+
"actionIds": ["github.pull_request_get"]
|
|
30
|
+
},
|
|
31
|
+
"workspace": { "cwd": "/projects/product" },
|
|
32
|
+
"metadata": { "icon": "search" }
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Identity
|
|
37
|
+
|
|
38
|
+
`id` is a stable machine identity: 1–128 lowercase letters, numbers, dots,
|
|
39
|
+
underscores, or hyphens. Renaming `name` never changes it. Deleted ids remain
|
|
40
|
+
reserved so historical sessions cannot be confused with a new agent.
|
|
41
|
+
|
|
42
|
+
## Revisions
|
|
43
|
+
|
|
44
|
+
The normalized complete definition is hashed. An edit with equal content keeps
|
|
45
|
+
the current revision. Any changed field creates a new immutable revision.
|
|
46
|
+
Sessions always retain the revision selected at creation.
|
|
47
|
+
|
|
48
|
+
## PATCH behavior
|
|
49
|
+
|
|
50
|
+
Top-level fields replace their prior values. Known nested objects—`driver`,
|
|
51
|
+
`resources`, `toolbox`, and `workspace`—merge by field, so a patch containing
|
|
52
|
+
only `resources.skills` preserves files and subagent references. Arrays are
|
|
53
|
+
authoritative and de-duplicated. Set nullable fields such as `model` or
|
|
54
|
+
`authRef` to `null` to clear them.
|
|
55
|
+
|
|
56
|
+
## References
|
|
57
|
+
|
|
58
|
+
- `authRef` names a credential the host resolves at execution time.
|
|
59
|
+
- `toolbox` matches the selection concepts in `@amalgm/tools` but does not
|
|
60
|
+
import or mutate a Toolbox.
|
|
61
|
+
- `resources.skills` and `resources.files` are resolved by the host.
|
|
62
|
+
- `resources.subagents` describes intended relationships; the host still
|
|
63
|
+
authorizes every agent-to-agent call.
|
|
64
|
+
- `driver.config` is non-secret runtime configuration interpreted only by the
|
|
65
|
+
selected driver.
|
package/docs/DRIVERS.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Agent drivers
|
|
2
|
+
|
|
3
|
+
Drivers connect durable Agents state to a native or hosted runtime. They are
|
|
4
|
+
registered by the embedding process and selected by `definition.driver.id`.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { defineDriver } from '@amalgm/agents';
|
|
8
|
+
|
|
9
|
+
export default defineDriver({
|
|
10
|
+
id: 'my-runtime',
|
|
11
|
+
async run(request, context) {
|
|
12
|
+
context.signal.throwIfAborted();
|
|
13
|
+
context.emit({ type: 'text.delta', data: { text: 'Working…' } });
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
driverSessionId: request.driverSessionId ?? 'native-session-id',
|
|
17
|
+
message: {
|
|
18
|
+
role: 'assistant',
|
|
19
|
+
parts: [{ type: 'text', text: 'Done.' }],
|
|
20
|
+
},
|
|
21
|
+
metadata: { provider: 'example' },
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Request
|
|
28
|
+
|
|
29
|
+
`DriverRunRequest` includes:
|
|
30
|
+
|
|
31
|
+
- the immutable `AgentRevision`;
|
|
32
|
+
- the durable session and current turn;
|
|
33
|
+
- ordered completed history before this turn;
|
|
34
|
+
- the accepted input; and
|
|
35
|
+
- the opaque `driverSessionId` returned by an earlier turn.
|
|
36
|
+
|
|
37
|
+
Drivers do not receive a database handle. They resolve Toolbox actions,
|
|
38
|
+
credentials, skills, files, and native processes through dependencies supplied
|
|
39
|
+
by their host.
|
|
40
|
+
|
|
41
|
+
## Events and results
|
|
42
|
+
|
|
43
|
+
Call `context.emit` for streaming or structured observations. The event is
|
|
44
|
+
committed before the call returns. Use type `message` with an assistant
|
|
45
|
+
`AgentMessage` for a durable output message; other event names are driver-owned
|
|
46
|
+
and their data must be JSON.
|
|
47
|
+
|
|
48
|
+
A final returned `message` is appended once before turn completion. Returned
|
|
49
|
+
`driverSessionId` replaces the prior opaque value for future continuation.
|
|
50
|
+
|
|
51
|
+
## Cancellation and limits
|
|
52
|
+
|
|
53
|
+
Drivers must observe `context.signal` and stop their underlying process or
|
|
54
|
+
request. The service records cancellation intent before aborting the signal.
|
|
55
|
+
Input and event sizes are bounded. A driver that emits invalid or oversized
|
|
56
|
+
data fails the turn explicitly.
|
|
57
|
+
|
|
58
|
+
The in-process boundary cannot forcibly kill an uncooperative driver. Process
|
|
59
|
+
drivers should own their child process and terminate it when signalled.
|
|
60
|
+
|
|
61
|
+
## Loading drivers in CLIs
|
|
62
|
+
|
|
63
|
+
Compile driver modules to JavaScript and export either an `AgentDriver`, a
|
|
64
|
+
`driver` value, or a zero-argument factory:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
AMALGM_AGENT_DRIVERS=./dist/codex-driver.js amalgm-agents-rest
|
|
68
|
+
amalgm-agents --drivers ./dist/codex-driver.js talk reviewer "Review this"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Multiple modules are comma-separated. Secret values belong in the host's
|
|
72
|
+
credential resolver, not in agent definitions or driver module arguments.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Amalgm Engine integration
|
|
2
|
+
|
|
3
|
+
Engine remains the composition root. This package becomes the canonical agent
|
|
4
|
+
registry and session ledger; Engine supplies adapters for everything outside
|
|
5
|
+
that domain.
|
|
6
|
+
|
|
7
|
+
## Existing ownership to replace
|
|
8
|
+
|
|
9
|
+
| Current Engine area | Destination |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `amalgm-mcp/agents/store.js` agent rows | `AgentStore` through `Agents` |
|
|
12
|
+
| `amalgm-mcp/agent-config/*` normalized config | immutable agent definitions |
|
|
13
|
+
| `amalgm-mcp/agents/talk.js` durable sessions and logs | Agents sessions, turns, events |
|
|
14
|
+
| `amalgm-mcp/agents/tools.js` | packaged MCP tools |
|
|
15
|
+
| `amalgm-mcp/agents/rest.js` and route wiring | packaged REST adapter |
|
|
16
|
+
| `chat-core` native runtime execution | Engine-supplied `AgentDriver` implementations |
|
|
17
|
+
| Toolbox lookup and MCP resolution | `@amalgm/tools` adapter inside drivers |
|
|
18
|
+
| credential adapter | `authRef` resolver inside drivers |
|
|
19
|
+
| Supabase Chat session creation | Chat/cloud adapter observing Agents events |
|
|
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.
|
|
24
|
+
|
|
25
|
+
## Required driver adapter
|
|
26
|
+
|
|
27
|
+
Engine should implement one driver per native harness identity (`codex`,
|
|
28
|
+
`claude_code`, `opencode`, `pi`, and others). A driver translates the immutable
|
|
29
|
+
revision into chat-core's runtime envelope, resolves `authRef`, projects the
|
|
30
|
+
Toolbox loadout, forwards normalized streaming events, and returns the native
|
|
31
|
+
session id for continuation.
|
|
32
|
+
|
|
33
|
+
Drivers must not write Agents SQLite or invent another conversation log.
|
|
34
|
+
|
|
35
|
+
## Chat boundary
|
|
36
|
+
|
|
37
|
+
Engine may associate an Agents session id with a Chat record. Chat owns title,
|
|
38
|
+
participants, layout, read state, and cloud presentation. Agents owns pinned
|
|
39
|
+
revision, accepted inputs, driver execution, and events. Synchronization is an
|
|
40
|
+
adapter subscribed to committed Agents events; neither database is imported by
|
|
41
|
+
the other product.
|
|
42
|
+
|
|
43
|
+
## One-time migration
|
|
44
|
+
|
|
45
|
+
1. Stop the legacy agent mutation and talk surfaces.
|
|
46
|
+
2. Read each legacy agent plus its `agent_config` as one complete definition.
|
|
47
|
+
3. Map `baseHarnessId` to `driver.id`, model fields to `model`, auth mode to an
|
|
48
|
+
`authRef`, and legacy loadout ids to `toolbox.toolIds`.
|
|
49
|
+
4. Create missing agents and update existing agents through `Agents`.
|
|
50
|
+
5. Import local conversation JSONL as archived sessions only when its agent and
|
|
51
|
+
chronological message identity are unambiguous; otherwise retain it as a
|
|
52
|
+
legacy read-only export.
|
|
53
|
+
6. Start packaged REST and MCP adapters behind Engine's existing authenticated
|
|
54
|
+
routes.
|
|
55
|
+
7. Enable native drivers and verify continuation against real harness sessions.
|
|
56
|
+
8. Delete the legacy registry, config, talk, REST, and MCP implementations only
|
|
57
|
+
after parity and rollback checks pass.
|
|
58
|
+
|
|
59
|
+
Migration must be idempotent. Record the source identity and migration version
|
|
60
|
+
outside agent metadata or in a namespaced metadata field; never manufacture a
|
|
61
|
+
new revision on equal reruns.
|
|
62
|
+
|
|
63
|
+
## Integration tests
|
|
64
|
+
|
|
65
|
+
Before switchover, run:
|
|
66
|
+
|
|
67
|
+
- legacy agent create/update/delete through the UI against `Agents`;
|
|
68
|
+
- a session created on revision 1, followed by an agent edit and continuation;
|
|
69
|
+
- native resume after Engine restart;
|
|
70
|
+
- Toolbox loadout enforcement through `@amalgm/tools`;
|
|
71
|
+
- provider authentication failure without credential persistence in Agents;
|
|
72
|
+
- agent-to-agent background calls and cancellation;
|
|
73
|
+
- Chat projection without Chat becoming the session source of truth; and
|
|
74
|
+
- one-time migration twice with byte-for-byte equal resulting definitions.
|
package/docs/MCP.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# MCP server
|
|
2
|
+
|
|
3
|
+
`amalgm-agents-mcp` is the standalone stdio server. `createMcpServer` from
|
|
4
|
+
`@amalgm/agents/mcp` embeds the same server.
|
|
5
|
+
|
|
6
|
+
It exposes seven tools:
|
|
7
|
+
|
|
8
|
+
| Tool | Purpose |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `agents_list` | List persistent agents |
|
|
11
|
+
| `agents_get` | Inspect one agent |
|
|
12
|
+
| `agents_create` | Create an agent |
|
|
13
|
+
| `agents_update` | Edit an agent |
|
|
14
|
+
| `agents_delete` | Delete an agent while retaining session history |
|
|
15
|
+
| `agents_get_conversation` | Inspect a durable agent session |
|
|
16
|
+
| `talk_to_agent` | Start or continue an agent session |
|
|
17
|
+
|
|
18
|
+
`talk_to_agent` accepts the legacy `agent`, `conversation_id`, `description`,
|
|
19
|
+
and `prompt` fields. `agent_id`, `session_id`, and structured `message` are
|
|
20
|
+
also accepted. It returns `conversation_id` as the durable Agents session ID,
|
|
21
|
+
not a Chat ID. Use `run_in_background` for long work.
|
|
22
|
+
|
|
23
|
+
All handlers call the injected `Agents` service. MCP contains no second
|
|
24
|
+
registry, transcript, execution engine, or routing subsystem.
|
package/docs/REST.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# REST API
|
|
2
|
+
|
|
3
|
+
Create an embeddable server with `createRestServer` from
|
|
4
|
+
`@amalgm/agents/http`, or run `amalgm-agents-rest`.
|
|
5
|
+
|
|
6
|
+
The CLI default is `127.0.0.1:4317`. If a token is configured, all `/v1`
|
|
7
|
+
requests require the token via `Authorization: Bearer <token>` (scheme
|
|
8
|
+
case-insensitive) or the `x-amalgm-runtime-token` header. A missing or wrong
|
|
9
|
+
token receives the kernel's canonical 401 from `@amalgm/core/transport`,
|
|
10
|
+
compared in constant time. `/healthz` is a minimal unauthenticated liveness
|
|
11
|
+
response. JSON bodies default to a 512 KB limit.
|
|
12
|
+
|
|
13
|
+
## Agents
|
|
14
|
+
|
|
15
|
+
| Method | Path | Result |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `GET` | `/v1/agents` | List live agents |
|
|
18
|
+
| `POST` | `/v1/agents` | Create from complete body |
|
|
19
|
+
| `GET` | `/v1/agents/:id` | Read current definition |
|
|
20
|
+
| `PATCH` | `/v1/agents/:id` | Merge a definition patch |
|
|
21
|
+
| `DELETE` | `/v1/agents/:id` | Tombstone agent |
|
|
22
|
+
|
|
23
|
+
Use `?include_deleted=true` on list or get to include tombstones.
|
|
24
|
+
|
|
25
|
+
## Sessions
|
|
26
|
+
|
|
27
|
+
| Method | Path | Result |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `GET` | `/v1/sessions` | List sessions |
|
|
30
|
+
| `POST` | `/v1/sessions` | Start `{ agentId, revisionId?, sessionId?, metadata? }` |
|
|
31
|
+
| `GET` | `/v1/sessions/:id` | Read session |
|
|
32
|
+
| `DELETE` | `/v1/sessions/:id` | Archive inactive session |
|
|
33
|
+
| `GET` | `/v1/sessions/:id/turns` | List turns |
|
|
34
|
+
| `GET` | `/v1/sessions/:id/events` | List ordered events |
|
|
35
|
+
| `POST` | `/v1/sessions/:id/messages` | Send a turn |
|
|
36
|
+
| `POST` | `/v1/sessions/:id/cancel` | Cancel active turn |
|
|
37
|
+
|
|
38
|
+
List filters are `agent_id` and `include_archived=true`. Event pagination uses
|
|
39
|
+
`after` and `limit` (maximum 1000).
|
|
40
|
+
|
|
41
|
+
Send a message:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"message": "Review the current diff.",
|
|
46
|
+
"idempotencyKey": "review-001",
|
|
47
|
+
"wait": true
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`message` may also be a structured `AgentMessage`. `wait: false` returns `202`
|
|
52
|
+
after durable acceptance; otherwise the response waits for a terminal turn.
|
|
53
|
+
|
|
54
|
+
## Server-sent events
|
|
55
|
+
|
|
56
|
+
Connect to:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
GET /v1/sessions/:id/events/stream?after=<last-sequence>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The stream sends committed backlog first and then live events. SSE `id` equals
|
|
63
|
+
the session sequence. Reconnect with the last seen id as `after`. Heartbeat
|
|
64
|
+
comments are sent every 15 seconds.
|
|
65
|
+
|
|
66
|
+
## Errors
|
|
67
|
+
|
|
68
|
+
Errors use the HTTP status associated with `AgentError`:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"error": {
|
|
73
|
+
"code": "conflict",
|
|
74
|
+
"message": "Session already has an active turn.",
|
|
75
|
+
"details": {}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|