@amalgm/agents 0.2.1 → 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 +25 -24
- package/README.md +21 -47
- package/dist/agents.d.ts +4 -26
- package/dist/agents.js +4 -87
- package/dist/bin/mcp.js +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +2 -13
- package/dist/cli/open.d.ts +1 -1
- package/dist/cli/open.js +1 -5
- package/dist/cli/run.js +3 -8
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +0 -2
- package/dist/http/server.js +1 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/mcp/agent-tools.js +1 -1
- package/dist/mcp/tools.js +1 -2
- package/dist/rows.d.ts +1 -4
- package/dist/rows.js +0 -39
- package/dist/schema.js +53 -101
- package/dist/types.d.ts +0 -106
- package/docs/ARCHITECTURE.md +21 -33
- package/docs/CLI.md +3 -43
- package/docs/DATA_MODEL.md +7 -46
- package/docs/DEFINITIONS.md +4 -4
- package/docs/ENGINE_INTEGRATION.md +9 -71
- package/docs/MCP.md +3 -19
- package/docs/REST.md +14 -97
- package/docs/SDK.md +12 -61
- package/docs/SECURITY.md +6 -35
- package/docs/SHELL_INTEGRATION.md +29 -0
- package/examples/basic.ts +8 -22
- package/package.json +2 -2
- package/skills/amalgm-agents/SKILL.md +231 -20
- package/skills/amalgm-agents/agents/openai.yaml +2 -2
- package/dist/cli/session-commands.d.ts +0 -4
- package/dist/cli/session-commands.js +0 -54
- package/dist/drivers.d.ts +0 -4
- package/dist/drivers.js +0 -25
- package/dist/event-store.d.ts +0 -13
- package/dist/event-store.js +0 -50
- package/dist/http/session-routes.d.ts +0 -2
- package/dist/http/session-routes.js +0 -67
- package/dist/http/stream.d.ts +0 -3
- package/dist/http/stream.js +0 -30
- package/dist/mcp/session-tools.d.ts +0 -3
- package/dist/mcp/session-tools.js +0 -81
- package/dist/messages.d.ts +0 -3
- package/dist/messages.js +0 -56
- package/dist/runtime.d.ts +0 -29
- package/dist/runtime.js +0 -176
- package/dist/session-store.d.ts +0 -15
- package/dist/session-store.js +0 -83
- package/dist/turn-store.d.ts +0 -31
- package/dist/turn-store.js +0 -164
- package/docs/DRIVERS.md +0 -72
package/dist/turn-store.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type Database from 'better-sqlite3';
|
|
2
|
-
import type { EventStore } from './event-store.js';
|
|
3
|
-
import type { SessionStore } from './session-store.js';
|
|
4
|
-
import type { AgentFailure, AgentMessage, AgentTurn, JsonObject } from './types.js';
|
|
5
|
-
export declare class TurnStore {
|
|
6
|
-
private readonly database;
|
|
7
|
-
private readonly sessions;
|
|
8
|
-
private readonly events;
|
|
9
|
-
private readonly now;
|
|
10
|
-
constructor(database: Database.Database, sessions: SessionStore, events: EventStore, now: () => string);
|
|
11
|
-
reserve(sessionId: string, input: AgentMessage, key?: string): {
|
|
12
|
-
turn: AgentTurn;
|
|
13
|
-
duplicate: boolean;
|
|
14
|
-
};
|
|
15
|
-
markRunning(id: string): AgentTurn;
|
|
16
|
-
requestCancellation(id: string): AgentTurn;
|
|
17
|
-
complete(id: string, result: JsonObject): AgentTurn;
|
|
18
|
-
fail(id: string, failure: AgentFailure): AgentTurn;
|
|
19
|
-
cancel(id: string): AgentTurn;
|
|
20
|
-
interrupt(id: string, failure: AgentFailure): AgentTurn;
|
|
21
|
-
get(id: string): AgentTurn | null;
|
|
22
|
-
require(id: string): AgentTurn;
|
|
23
|
-
list(sessionId: string): AgentTurn[];
|
|
24
|
-
active(sessionId: string): AgentTurn | null;
|
|
25
|
-
history(sessionId: string, beforeTurnId?: string): AgentMessage[];
|
|
26
|
-
recoverInterrupted(): number;
|
|
27
|
-
private byKey;
|
|
28
|
-
private transition;
|
|
29
|
-
private terminal;
|
|
30
|
-
private touch;
|
|
31
|
-
}
|
package/dist/turn-store.js
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { AgentError } from './errors.js';
|
|
2
|
-
import { newId, optionalKey, randomKey, requireId } from './ids.js';
|
|
3
|
-
import { isObject } from './json.js';
|
|
4
|
-
import { turnFromRow } from './rows.js';
|
|
5
|
-
const ACTIVE_SQL = `status IN ('queued', 'running', 'cancelling')`;
|
|
6
|
-
const TERMINAL = new Set(['completed', 'failed', 'cancelled', 'interrupted']);
|
|
7
|
-
export class TurnStore {
|
|
8
|
-
database;
|
|
9
|
-
sessions;
|
|
10
|
-
events;
|
|
11
|
-
now;
|
|
12
|
-
constructor(database, sessions, events, now) {
|
|
13
|
-
this.database = database;
|
|
14
|
-
this.sessions = sessions;
|
|
15
|
-
this.events = events;
|
|
16
|
-
this.now = now;
|
|
17
|
-
}
|
|
18
|
-
reserve(sessionId, input, key) {
|
|
19
|
-
const session = this.sessions.require(sessionId);
|
|
20
|
-
if (session.status !== 'active')
|
|
21
|
-
throw new AgentError('conflict', 'Session is archived.');
|
|
22
|
-
const idempotencyKey = optionalKey(key) || randomKey('turn-key');
|
|
23
|
-
const existing = this.byKey(session.id, idempotencyKey);
|
|
24
|
-
if (existing)
|
|
25
|
-
return { turn: existing, duplicate: true };
|
|
26
|
-
if (this.active(session.id))
|
|
27
|
-
throw new AgentError('conflict', 'Session already has an active turn.');
|
|
28
|
-
const id = newId('turn');
|
|
29
|
-
const timestamp = this.now();
|
|
30
|
-
let event;
|
|
31
|
-
try {
|
|
32
|
-
this.database.transaction(() => {
|
|
33
|
-
this.database.prepare(`
|
|
34
|
-
INSERT INTO turns (id, session_id, idempotency_key, status, input_json, created_at)
|
|
35
|
-
VALUES (?, ?, ?, 'queued', ?, ?)
|
|
36
|
-
`).run(id, session.id, idempotencyKey, JSON.stringify(input), timestamp);
|
|
37
|
-
event = this.events.write(session.id, id, 'turn.input', { message: input });
|
|
38
|
-
this.touch(session.id, timestamp);
|
|
39
|
-
})();
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
const racedDuplicate = this.byKey(session.id, idempotencyKey);
|
|
43
|
-
if (racedDuplicate)
|
|
44
|
-
return { turn: racedDuplicate, duplicate: true };
|
|
45
|
-
if (isUniqueConstraint(error)) {
|
|
46
|
-
throw new AgentError('conflict', 'Session already has an active turn.');
|
|
47
|
-
}
|
|
48
|
-
throw error;
|
|
49
|
-
}
|
|
50
|
-
if (event)
|
|
51
|
-
this.events.announce(event);
|
|
52
|
-
return { turn: this.require(id), duplicate: false };
|
|
53
|
-
}
|
|
54
|
-
markRunning(id) {
|
|
55
|
-
return this.transition(id, 'running', 'turn.running', true);
|
|
56
|
-
}
|
|
57
|
-
requestCancellation(id) {
|
|
58
|
-
return this.transition(id, 'cancelling', 'turn.cancelling');
|
|
59
|
-
}
|
|
60
|
-
complete(id, result) {
|
|
61
|
-
return this.terminal(id, 'completed', result, null, 'turn.completed');
|
|
62
|
-
}
|
|
63
|
-
fail(id, failure) {
|
|
64
|
-
return this.terminal(id, 'failed', null, failure, 'turn.failed');
|
|
65
|
-
}
|
|
66
|
-
cancel(id) {
|
|
67
|
-
return this.terminal(id, 'cancelled', null, null, 'turn.cancelled');
|
|
68
|
-
}
|
|
69
|
-
interrupt(id, failure) {
|
|
70
|
-
return this.terminal(id, 'interrupted', null, failure, 'turn.interrupted');
|
|
71
|
-
}
|
|
72
|
-
get(id) {
|
|
73
|
-
const row = this.database.prepare('SELECT * FROM turns WHERE id = ?').get(requireId(id, 'turn id'));
|
|
74
|
-
return row ? turnFromRow(row) : null;
|
|
75
|
-
}
|
|
76
|
-
require(id) {
|
|
77
|
-
const turn = this.get(id);
|
|
78
|
-
if (!turn)
|
|
79
|
-
throw new AgentError('not_found', `Turn not found: ${id}`);
|
|
80
|
-
return turn;
|
|
81
|
-
}
|
|
82
|
-
list(sessionId) {
|
|
83
|
-
this.sessions.require(sessionId);
|
|
84
|
-
return this.database.prepare('SELECT * FROM turns WHERE session_id = ? ORDER BY created_at ASC')
|
|
85
|
-
.all(sessionId).map(turnFromRow);
|
|
86
|
-
}
|
|
87
|
-
active(sessionId) {
|
|
88
|
-
const row = this.database.prepare(`
|
|
89
|
-
SELECT * FROM turns WHERE session_id = ? AND ${ACTIVE_SQL} ORDER BY created_at DESC LIMIT 1
|
|
90
|
-
`).get(sessionId);
|
|
91
|
-
return row ? turnFromRow(row) : null;
|
|
92
|
-
}
|
|
93
|
-
history(sessionId, beforeTurnId) {
|
|
94
|
-
const messages = [];
|
|
95
|
-
for (const turn of this.list(sessionId)) {
|
|
96
|
-
if (turn.id === beforeTurnId)
|
|
97
|
-
break;
|
|
98
|
-
messages.push(turn.input);
|
|
99
|
-
for (const event of this.events.forTurn(turn.id)) {
|
|
100
|
-
const message = isObject(event.data.message) ? event.data.message : null;
|
|
101
|
-
if (event.type === 'message' && message)
|
|
102
|
-
messages.push(message);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return messages;
|
|
106
|
-
}
|
|
107
|
-
recoverInterrupted() {
|
|
108
|
-
const rows = this.database.prepare(`SELECT * FROM turns WHERE ${ACTIVE_SQL}`).all();
|
|
109
|
-
for (const row of rows) {
|
|
110
|
-
this.interrupt(turnFromRow(row).id, {
|
|
111
|
-
code: 'process_restarted',
|
|
112
|
-
message: 'The owning process restarted before the driver settled.',
|
|
113
|
-
details: {},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
return rows.length;
|
|
117
|
-
}
|
|
118
|
-
byKey(sessionId, key) {
|
|
119
|
-
const row = this.database.prepare('SELECT * FROM turns WHERE session_id = ? AND idempotency_key = ?')
|
|
120
|
-
.get(sessionId, key);
|
|
121
|
-
return row ? turnFromRow(row) : null;
|
|
122
|
-
}
|
|
123
|
-
transition(id, status, type, started = false) {
|
|
124
|
-
const turn = this.require(id);
|
|
125
|
-
const timestamp = this.now();
|
|
126
|
-
let event;
|
|
127
|
-
this.database.transaction(() => {
|
|
128
|
-
this.database.prepare(`UPDATE turns SET status = ?, started_at = COALESCE(started_at, ?) WHERE id = ?`)
|
|
129
|
-
.run(status, started ? timestamp : turn.startedAt, turn.id);
|
|
130
|
-
event = this.events.write(turn.sessionId, turn.id, type, {});
|
|
131
|
-
this.touch(turn.sessionId, timestamp);
|
|
132
|
-
})();
|
|
133
|
-
if (event)
|
|
134
|
-
this.events.announce(event);
|
|
135
|
-
return this.require(turn.id);
|
|
136
|
-
}
|
|
137
|
-
terminal(id, status, result, error, type) {
|
|
138
|
-
const turn = this.require(id);
|
|
139
|
-
if (TERMINAL.has(turn.status))
|
|
140
|
-
return turn;
|
|
141
|
-
const timestamp = this.now();
|
|
142
|
-
let event;
|
|
143
|
-
this.database.transaction(() => {
|
|
144
|
-
this.database.prepare(`
|
|
145
|
-
UPDATE turns SET status = ?, result_json = ?, error_json = ?, completed_at = ? WHERE id = ?
|
|
146
|
-
`).run(status, result && JSON.stringify(result), error && JSON.stringify(error), timestamp, turn.id);
|
|
147
|
-
const data = error ? { error: error } : {};
|
|
148
|
-
event = this.events.write(turn.sessionId, turn.id, type, data);
|
|
149
|
-
this.touch(turn.sessionId, timestamp);
|
|
150
|
-
})();
|
|
151
|
-
if (event)
|
|
152
|
-
this.events.announce(event);
|
|
153
|
-
return this.require(turn.id);
|
|
154
|
-
}
|
|
155
|
-
touch(sessionId, timestamp) {
|
|
156
|
-
this.database.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(timestamp, sessionId);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function isUniqueConstraint(error) {
|
|
160
|
-
return Boolean(error
|
|
161
|
-
&& typeof error === 'object'
|
|
162
|
-
&& 'code' in error
|
|
163
|
-
&& String(error.code).startsWith('SQLITE_CONSTRAINT'));
|
|
164
|
-
}
|
package/docs/DRIVERS.md
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
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.
|