@amalgm/agents 0.1.3 → 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.
- package/PURPOSE.md +25 -24
- package/README.md +19 -46
- 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 +22 -67
- 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/examples/basic.ts +8 -22
- package/package.json +2 -2
- package/skills/amalgm-agents/SKILL.md +3 -12
- 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/docs/REST.md
CHANGED
|
@@ -1,103 +1,20 @@
|
|
|
1
1
|
# REST API
|
|
2
2
|
|
|
3
|
-
Create an embeddable server with `createRestServer` from
|
|
4
|
-
|
|
3
|
+
Create an embeddable server with `createRestServer` from `@amalgm/agents/http`,
|
|
4
|
+
or run `amalgm-agents-rest`. The CLI binds `127.0.0.1:4317` by default.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
## 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
|
-
|
|
38
|
-
## Agents
|
|
6
|
+
When configured, authentication uses `Authorization: Bearer <token>` or the
|
|
7
|
+
`x-amalgm-runtime-token` header. `/healthz` is unauthenticated. JSON bodies
|
|
8
|
+
default to a 512 KB limit.
|
|
39
9
|
|
|
40
10
|
| Method | Path | Result |
|
|
41
11
|
|---|---|---|
|
|
42
|
-
| `GET` | `/v1/agents` | List
|
|
43
|
-
| `POST` | `/v1/agents` | Create from complete
|
|
44
|
-
| `GET` | `/v1/agents/:id` | Read current definition |
|
|
45
|
-
| `PATCH` | `/v1/agents/:id` |
|
|
46
|
-
| `DELETE` | `/v1/agents/:id` | Tombstone
|
|
47
|
-
|
|
48
|
-
Use `?include_deleted=true` on list or get to include tombstones.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
| Method | Path | Result |
|
|
53
|
-
|---|---|---|
|
|
54
|
-
| `GET` | `/v1/sessions` | List sessions |
|
|
55
|
-
| `POST` | `/v1/sessions` | Start `{ agentId, revisionId?, sessionId?, metadata? }` |
|
|
56
|
-
| `GET` | `/v1/sessions/:id` | Read session |
|
|
57
|
-
| `DELETE` | `/v1/sessions/:id` | Archive inactive session |
|
|
58
|
-
| `GET` | `/v1/sessions/:id/turns` | List turns |
|
|
59
|
-
| `GET` | `/v1/sessions/:id/events` | List ordered events |
|
|
60
|
-
| `POST` | `/v1/sessions/:id/messages` | Send a turn |
|
|
61
|
-
| `POST` | `/v1/sessions/:id/cancel` | Cancel active turn |
|
|
62
|
-
|
|
63
|
-
List filters are `agent_id` and `include_archived=true`. Event pagination uses
|
|
64
|
-
`after` and `limit` (maximum 1000).
|
|
65
|
-
|
|
66
|
-
Send a message:
|
|
67
|
-
|
|
68
|
-
```json
|
|
69
|
-
{
|
|
70
|
-
"message": "Review the current diff.",
|
|
71
|
-
"idempotencyKey": "review-001",
|
|
72
|
-
"wait": true
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
`message` may also be a structured `AgentMessage`. `wait: false` returns `202`
|
|
77
|
-
after durable acceptance; otherwise the response waits for a terminal turn.
|
|
78
|
-
|
|
79
|
-
## Server-sent events
|
|
80
|
-
|
|
81
|
-
Connect to:
|
|
82
|
-
|
|
83
|
-
```text
|
|
84
|
-
GET /v1/sessions/:id/events/stream?after=<last-sequence>
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
The stream sends committed backlog first and then live events. SSE `id` equals
|
|
88
|
-
the session sequence. Reconnect with the last seen id as `after`. Heartbeat
|
|
89
|
-
comments are sent every 15 seconds.
|
|
90
|
-
|
|
91
|
-
## Errors
|
|
92
|
-
|
|
93
|
-
Errors use the HTTP status associated with `AgentError`:
|
|
94
|
-
|
|
95
|
-
```json
|
|
96
|
-
{
|
|
97
|
-
"error": {
|
|
98
|
-
"code": "conflict",
|
|
99
|
-
"message": "Session already has an active turn.",
|
|
100
|
-
"details": {}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
```
|
|
12
|
+
| `GET` | `/v1/agents` | List available agents |
|
|
13
|
+
| `POST` | `/v1/agents` | Create from a complete definition |
|
|
14
|
+
| `GET` | `/v1/agents/:id` | Read the current definition |
|
|
15
|
+
| `PATCH` | `/v1/agents/:id` | Apply a definition patch |
|
|
16
|
+
| `DELETE` | `/v1/agents/:id` | Tombstone an identity |
|
|
17
|
+
|
|
18
|
+
Use `?include_deleted=true` on list or get to include tombstones. The same
|
|
19
|
+
server also exposes agent configuration, installed skills, and bundle routes
|
|
20
|
+
documented by their SDK modules. It exposes no conversation routes.
|
package/docs/SDK.md
CHANGED
|
@@ -1,72 +1,23 @@
|
|
|
1
1
|
# SDK reference
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
durable before
|
|
5
|
-
|
|
6
|
-
## Construction
|
|
3
|
+
`Agents` is the public behavior boundary for the local agent registry. Every
|
|
4
|
+
mutation is durable before it returns.
|
|
7
5
|
|
|
8
6
|
```ts
|
|
9
|
-
const agents = new Agents({
|
|
10
|
-
stateDir: './state',
|
|
11
|
-
drivers: [codexDriver],
|
|
12
|
-
maxInputBytes: 256_000,
|
|
13
|
-
maxEventBytes: 256_000,
|
|
14
|
-
turnTimeoutMs: 600_000,
|
|
15
|
-
});
|
|
7
|
+
const agents = new Agents({ stateDir: './state' });
|
|
16
8
|
```
|
|
17
9
|
|
|
18
|
-
`databasePath` overrides the database location directly. `
|
|
19
|
-
|
|
20
|
-
for them to settle, and close SQLite.
|
|
21
|
-
|
|
22
|
-
## Agent methods
|
|
10
|
+
`databasePath` overrides the database location directly. Call `close()` when
|
|
11
|
+
the embedding process releases the registry.
|
|
23
12
|
|
|
24
13
|
| Method | Behavior |
|
|
25
14
|
|---|---|
|
|
26
|
-
| `createAgent(definition)` | Create identity and revision 1
|
|
27
|
-
| `updateAgent(id, patch)` |
|
|
28
|
-
| `deleteAgent(id)` | Tombstone the identity while retaining
|
|
15
|
+
| `createAgent(definition)` | Create an identity and immutable revision 1 |
|
|
16
|
+
| `updateAgent(id, patch)` | Apply a patch, appending a revision when content changes |
|
|
17
|
+
| `deleteAgent(id)` | Tombstone the identity while retaining its revisions |
|
|
29
18
|
| `getAgent(id, includeDeleted?)` | Read the current definition |
|
|
30
|
-
| `listAgents(includeDeleted?)` | List
|
|
31
|
-
|
|
32
|
-
## Session methods
|
|
33
|
-
|
|
34
|
-
| Method | Behavior |
|
|
35
|
-
|---|---|
|
|
36
|
-
| `startSession(input)` | Pin a new session to the current or named revision |
|
|
37
|
-
| `talk(agentId, input)` | Start or continue a session, then enqueue a turn |
|
|
38
|
-
| `enqueue(sessionId, input)` | Return immediately with the turn and completion promise |
|
|
39
|
-
| `send(sessionId, input)` | Wait for the turn's terminal state |
|
|
40
|
-
| `cancelSession(id)` | Persist cancellation intent and signal its active driver |
|
|
41
|
-
| `archiveSession(id)` | Archive an inactive session |
|
|
42
|
-
| `listSessions(agentId?, includeArchived?)` | Discover durable sessions |
|
|
43
|
-
| `listTurns(sessionId)` | Read accepted inputs and outcomes |
|
|
44
|
-
| `listEvents(sessionId, after?, limit?)` | Read ordered events |
|
|
45
|
-
|
|
46
|
-
`enqueue` returns `{ turn, completion, duplicate }`. If an idempotency key was
|
|
47
|
-
already accepted, `duplicate` is true and the original turn is returned.
|
|
48
|
-
|
|
49
|
-
## Event subscription
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
const unsubscribe = agents.subscribe(session.id, (event) => {
|
|
53
|
-
console.log(event.sequence, event.type, event.data);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
await agents.send(session.id, { message: 'Hello' });
|
|
57
|
-
unsubscribe();
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Listeners receive events only after commit. To build a reconnectable consumer,
|
|
61
|
-
read `listEvents(after)` before subscribing and de-duplicate by sequence, as
|
|
62
|
-
the packaged SSE adapter does.
|
|
63
|
-
|
|
64
|
-
## Errors
|
|
65
|
-
|
|
66
|
-
Expected failures use `AgentError` with a stable code:
|
|
67
|
-
|
|
68
|
-
`invalid_input`, `not_found`, `conflict`, `deleted`, `driver_unavailable`,
|
|
69
|
-
`too_large`, or `cancelled`.
|
|
19
|
+
| `listAgents(includeDeleted?)` | List available agents by default |
|
|
20
|
+
| `getAgentRevision(agentId, revisionId?)` | Resolve the current or exact immutable revision |
|
|
70
21
|
|
|
71
|
-
|
|
72
|
-
|
|
22
|
+
Expected failures use `AgentError` with stable codes: `invalid_input`,
|
|
23
|
+
`not_found`, `conflict`, `deleted`, and `too_large`.
|
package/docs/SECURITY.md
CHANGED
|
@@ -1,38 +1,9 @@
|
|
|
1
1
|
# Security model
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Agent definitions contain `authRef`, never credentials. Toolbox ids, skill ids,
|
|
4
|
+
file references, and subagent ids are requests, not authority; Shell resolves
|
|
5
|
+
and authorizes them while preparing a Chat execution.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
reference at execution time through the host's credential boundary. Do not put
|
|
10
|
-
tokens, cookies, private keys, or passwords in `driver.config`, `metadata`,
|
|
11
|
-
messages, or events.
|
|
12
|
-
|
|
13
|
-
## Capability references
|
|
14
|
-
|
|
15
|
-
Toolbox ids and subagent ids are requests, not authority. The composing host
|
|
16
|
-
must resolve them against the caller's allowed Toolbox view and agent policy.
|
|
17
|
-
Agents never infers permission merely because an id appears in a definition.
|
|
18
|
-
|
|
19
|
-
## REST exposure
|
|
20
|
-
|
|
21
|
-
The REST CLI binds `127.0.0.1` by default. Configure a bearer token before
|
|
22
|
-
binding to a LAN, container bridge, tunnel, or public interface. The packaged
|
|
23
|
-
server intentionally does not implement browser sessions, CORS, TLS, user
|
|
24
|
-
accounts, or cloud authorization; Engine supplies those outer boundaries.
|
|
25
|
-
|
|
26
|
-
## Driver trust
|
|
27
|
-
|
|
28
|
-
Drivers execute inside the hosting process and are trusted code. A driver can
|
|
29
|
-
read its process environment and access the filesystem with that process's
|
|
30
|
-
permissions. Load only reviewed modules. Native process drivers should avoid a
|
|
31
|
-
shell, pass explicit arguments, bound output, and kill their child on abort.
|
|
32
|
-
|
|
33
|
-
## Stored data
|
|
34
|
-
|
|
35
|
-
SQLite files are created beneath a mode-`0700` state directory. Application
|
|
36
|
-
packaging and backups must preserve appropriate filesystem permissions.
|
|
37
|
-
Messages and driver events may contain sensitive user content; retention and
|
|
38
|
-
export policies belong to the embedding product.
|
|
7
|
+
The REST CLI binds loopback by default. Configure a bearer token before
|
|
8
|
+
binding it to a shared interface. SQLite state is created beneath a private
|
|
9
|
+
state directory and should be backed up with equivalent file permissions.
|
package/examples/basic.ts
CHANGED
|
@@ -1,31 +1,17 @@
|
|
|
1
|
-
import { Agents
|
|
1
|
+
import { Agents } from '@amalgm/agents';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
id: 'echo',
|
|
5
|
-
async run(request) {
|
|
6
|
-
return {
|
|
7
|
-
driverSessionId: request.driverSessionId || `echo-${request.session.id}`,
|
|
8
|
-
message: {
|
|
9
|
-
role: 'assistant',
|
|
10
|
-
parts: [{ type: 'text', text: `Echo: ${messageText(request.input)}` }],
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
},
|
|
14
|
-
});
|
|
3
|
+
const agents = new Agents({ stateDir: './example-state' });
|
|
15
4
|
|
|
16
|
-
|
|
17
|
-
const agents = new Agents({ stateDir: './example-state', drivers: [echo] });
|
|
5
|
+
try {
|
|
18
6
|
agents.createAgent({
|
|
19
7
|
id: 'reviewer',
|
|
20
8
|
name: 'Reviewer',
|
|
21
|
-
driver: '
|
|
9
|
+
driver: 'codex',
|
|
22
10
|
instructions: 'Review carefully.',
|
|
23
11
|
});
|
|
24
12
|
|
|
25
|
-
const
|
|
26
|
-
console.log(
|
|
27
|
-
|
|
28
|
-
|
|
13
|
+
const revision = agents.getAgentRevision('reviewer');
|
|
14
|
+
console.log(revision.id, revision.definition);
|
|
15
|
+
} finally {
|
|
16
|
+
agents.close();
|
|
29
17
|
}
|
|
30
|
-
|
|
31
|
-
void main();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/agents",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Local-first agent
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Local-first agent identities, installations, and immutable revisions.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
7
7
|
"repository": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: amalgm-agents
|
|
3
|
-
description: Create, inspect, edit, delete, and
|
|
3
|
+
description: Create, inspect, edit, delete, install, and resolve persistent Amalgm agent identities and immutable revisions. Use when a user wants to manage an agent definition or discover available agents.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Amalgm Agents
|
|
@@ -17,14 +17,5 @@ Use the available Agents MCP tools. If MCP is unavailable, use the equivalent
|
|
|
17
17
|
5. Keep tool, skill, file, credential, and subagent values as references; do
|
|
18
18
|
not copy those products into the agent definition.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
1. Resolve the agent with `agents_list` or `agents_get` when its identity is
|
|
23
|
-
ambiguous.
|
|
24
|
-
2. Call `talk_to_agent` without a session ID to start a session.
|
|
25
|
-
3. Preserve and return the resulting session ID.
|
|
26
|
-
4. Pass that ID to continue the same session.
|
|
27
|
-
5. Use conversation/session inspection to recover durable results or history.
|
|
28
|
-
6. Use background execution for work likely to outlive one MCP request.
|
|
29
|
-
|
|
30
|
-
Never create a Chat conversation merely to represent an Agents session.
|
|
20
|
+
To run an agent, resolve its identity here and use Chat. Agents has no
|
|
21
|
+
conversation, execution, transcript, interrupt, or reconnect operation.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Amalgm Agents"
|
|
3
|
-
short_description: "Create
|
|
4
|
-
default_prompt: "Use $amalgm-agents to create or
|
|
3
|
+
short_description: "Create and manage Amalgm agents"
|
|
4
|
+
default_prompt: "Use $amalgm-agents to create or manage an agent."
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { Agents } from '../agents.js';
|
|
2
|
-
import type { ParsedArgs } from './args.js';
|
|
3
|
-
export declare function runSessionCommand(agents: Agents, args: ParsedArgs): Promise<unknown>;
|
|
4
|
-
export declare function runTalkCommand(agents: Agents, args: ParsedArgs): Promise<unknown>;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { AgentError } from '../errors.js';
|
|
2
|
-
import { enabled, flag } from './args.js';
|
|
3
|
-
export async function runSessionCommand(agents, args) {
|
|
4
|
-
const [, action, first, second] = args.words;
|
|
5
|
-
if (action === 'list') {
|
|
6
|
-
return { sessions: agents.listSessions(flag(args, 'agent'), enabled(args, 'include-archived')) };
|
|
7
|
-
}
|
|
8
|
-
if (action === 'start' && first) {
|
|
9
|
-
const sessionId = flag(args, 'id');
|
|
10
|
-
const revisionId = flag(args, 'revision');
|
|
11
|
-
return { session: agents.startSession({
|
|
12
|
-
agentId: first,
|
|
13
|
-
...(sessionId ? { sessionId } : {}),
|
|
14
|
-
...(revisionId ? { revisionId } : {}),
|
|
15
|
-
}) };
|
|
16
|
-
}
|
|
17
|
-
if (action === 'show' && first) {
|
|
18
|
-
const session = agents.getSession(first);
|
|
19
|
-
if (!session)
|
|
20
|
-
throw new AgentError('not_found', `Session not found: ${first}`);
|
|
21
|
-
return { session, turns: agents.listTurns(first), events: agents.listEvents(first) };
|
|
22
|
-
}
|
|
23
|
-
if (action === 'events' && first) {
|
|
24
|
-
return { events: agents.listEvents(first, Number(flag(args, 'after') || 0)) };
|
|
25
|
-
}
|
|
26
|
-
if (action === 'send' && first && second) {
|
|
27
|
-
const idempotencyKey = flag(args, 'key');
|
|
28
|
-
return { turn: await agents.send(first, {
|
|
29
|
-
message: second,
|
|
30
|
-
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
31
|
-
}) };
|
|
32
|
-
}
|
|
33
|
-
if (action === 'cancel' && first)
|
|
34
|
-
return { turn: agents.cancelSession(first) };
|
|
35
|
-
if (action === 'archive' && first)
|
|
36
|
-
return { session: agents.archiveSession(first) };
|
|
37
|
-
throw new AgentError('invalid_input', 'Unknown or incomplete session command. Run with --help.');
|
|
38
|
-
}
|
|
39
|
-
export async function runTalkCommand(agents, args) {
|
|
40
|
-
const [, agentId, message] = args.words;
|
|
41
|
-
if (!agentId || !message)
|
|
42
|
-
throw new AgentError('invalid_input', 'talk requires agent id and message.');
|
|
43
|
-
const sessionId = flag(args, 'session');
|
|
44
|
-
const idempotencyKey = flag(args, 'key');
|
|
45
|
-
const call = agents.talk(agentId, {
|
|
46
|
-
message,
|
|
47
|
-
...(sessionId ? { sessionId } : {}),
|
|
48
|
-
...(idempotencyKey ? { idempotencyKey } : {}),
|
|
49
|
-
});
|
|
50
|
-
if (enabled(args, 'background')) {
|
|
51
|
-
return { session: call.session, turn: call.turn, duplicate: call.duplicate };
|
|
52
|
-
}
|
|
53
|
-
return { session: call.session, turn: await call.completion, duplicate: call.duplicate };
|
|
54
|
-
}
|
package/dist/drivers.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { AgentDriver } from './types.js';
|
|
2
|
-
export declare function defineDriver(driver: AgentDriver): AgentDriver;
|
|
3
|
-
export declare function loadDriverModules(specifiers: string[]): Promise<AgentDriver[]>;
|
|
4
|
-
export declare function driverSpecifiers(value?: string): string[];
|
package/dist/drivers.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
-
import { AgentError } from './errors.js';
|
|
4
|
-
export function defineDriver(driver) {
|
|
5
|
-
if (!driver || typeof driver.id !== 'string' || typeof driver.run !== 'function') {
|
|
6
|
-
throw new AgentError('invalid_input', 'Agent drivers require id and run.');
|
|
7
|
-
}
|
|
8
|
-
return driver;
|
|
9
|
-
}
|
|
10
|
-
export async function loadDriverModules(specifiers) {
|
|
11
|
-
const drivers = [];
|
|
12
|
-
for (const specifier of specifiers) {
|
|
13
|
-
const target = specifier.startsWith('.') || specifier.startsWith('/')
|
|
14
|
-
? pathToFileURL(path.resolve(specifier)).href
|
|
15
|
-
: specifier;
|
|
16
|
-
const loaded = await import(target);
|
|
17
|
-
const value = loaded.default ?? loaded.driver ?? loaded;
|
|
18
|
-
const driver = typeof value === 'function' ? await value() : value;
|
|
19
|
-
drivers.push(defineDriver(driver));
|
|
20
|
-
}
|
|
21
|
-
return drivers;
|
|
22
|
-
}
|
|
23
|
-
export function driverSpecifiers(value = process.env.AMALGM_AGENT_DRIVERS || '') {
|
|
24
|
-
return value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
25
|
-
}
|
package/dist/event-store.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type Database from 'better-sqlite3';
|
|
2
|
-
import type { EventListener, JsonObject, SessionEvent } from './types.js';
|
|
3
|
-
export declare class EventStore {
|
|
4
|
-
private readonly database;
|
|
5
|
-
private readonly now;
|
|
6
|
-
private readonly listener;
|
|
7
|
-
constructor(database: Database.Database, now: () => string, listener: EventListener);
|
|
8
|
-
append(sessionId: string, turnId: string | null, type: string, data: JsonObject): SessionEvent;
|
|
9
|
-
write(sessionId: string, turnId: string | null, type: string, data: JsonObject): SessionEvent;
|
|
10
|
-
announce(event: SessionEvent): void;
|
|
11
|
-
list(sessionId: string, after?: number, limit?: number): SessionEvent[];
|
|
12
|
-
forTurn(turnId: string): SessionEvent[];
|
|
13
|
-
}
|
package/dist/event-store.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { AgentError } from './errors.js';
|
|
2
|
-
import { newId } from './ids.js';
|
|
3
|
-
import { eventFromRow } from './rows.js';
|
|
4
|
-
export class EventStore {
|
|
5
|
-
database;
|
|
6
|
-
now;
|
|
7
|
-
listener;
|
|
8
|
-
constructor(database, now, listener) {
|
|
9
|
-
this.database = database;
|
|
10
|
-
this.now = now;
|
|
11
|
-
this.listener = listener;
|
|
12
|
-
}
|
|
13
|
-
append(sessionId, turnId, type, data) {
|
|
14
|
-
let event;
|
|
15
|
-
this.database.transaction(() => {
|
|
16
|
-
event = this.write(sessionId, turnId, type, data);
|
|
17
|
-
this.database.prepare('UPDATE sessions SET updated_at = ? WHERE id = ?').run(this.now(), sessionId);
|
|
18
|
-
})();
|
|
19
|
-
if (!event)
|
|
20
|
-
throw new AgentError('internal', 'Event transaction produced no event.');
|
|
21
|
-
this.announce(event);
|
|
22
|
-
return event;
|
|
23
|
-
}
|
|
24
|
-
write(sessionId, turnId, type, data) {
|
|
25
|
-
const sequence = Number(this.database.prepare(`
|
|
26
|
-
SELECT COALESCE(MAX(sequence), 0) + 1 AS value FROM session_events WHERE session_id = ?
|
|
27
|
-
`).get(sessionId).value);
|
|
28
|
-
const id = newId('event');
|
|
29
|
-
const createdAt = this.now();
|
|
30
|
-
this.database.prepare(`
|
|
31
|
-
INSERT INTO session_events (id, session_id, turn_id, sequence, type, data_json, created_at)
|
|
32
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
33
|
-
`).run(id, sessionId, turnId, sequence, type, JSON.stringify(data), createdAt);
|
|
34
|
-
return { id, sessionId, turnId, sequence, type, data, createdAt };
|
|
35
|
-
}
|
|
36
|
-
announce(event) {
|
|
37
|
-
this.listener(event);
|
|
38
|
-
}
|
|
39
|
-
list(sessionId, after = 0, limit = 200) {
|
|
40
|
-
const cursor = Number.isFinite(after) ? Math.max(0, Math.floor(after)) : 0;
|
|
41
|
-
const bounded = Number.isFinite(limit) ? Math.max(1, Math.min(1000, Math.floor(limit))) : 200;
|
|
42
|
-
return this.database.prepare(`
|
|
43
|
-
SELECT * FROM session_events WHERE session_id = ? AND sequence > ? ORDER BY sequence ASC LIMIT ?
|
|
44
|
-
`).all(sessionId, cursor, bounded).map(eventFromRow);
|
|
45
|
-
}
|
|
46
|
-
forTurn(turnId) {
|
|
47
|
-
return this.database.prepare('SELECT * FROM session_events WHERE turn_id = ? ORDER BY sequence ASC')
|
|
48
|
-
.all(turnId).map(eventFromRow);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { AgentError } from '../errors.js';
|
|
2
|
-
export async function routeSessions(context) {
|
|
3
|
-
const [resource, id, child] = context.path;
|
|
4
|
-
if (resource !== 'sessions')
|
|
5
|
-
return false;
|
|
6
|
-
if (!id && context.method === 'GET') {
|
|
7
|
-
const agentId = context.url.searchParams.get('agent_id') || undefined;
|
|
8
|
-
const archived = context.url.searchParams.get('include_archived') === 'true';
|
|
9
|
-
context.json(200, { sessions: context.agents.listSessions(agentId, archived) });
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
if (!id && context.method === 'POST') {
|
|
13
|
-
const body = await context.body();
|
|
14
|
-
if (typeof body.agentId !== 'string')
|
|
15
|
-
throw new AgentError('invalid_input', 'agentId is required.');
|
|
16
|
-
context.json(201, { session: context.agents.startSession({
|
|
17
|
-
agentId: body.agentId,
|
|
18
|
-
...(typeof body.revisionId === 'string' ? { revisionId: body.revisionId } : {}),
|
|
19
|
-
...(typeof body.sessionId === 'string' ? { sessionId: body.sessionId } : {}),
|
|
20
|
-
...(body.metadata ? { metadata: body.metadata } : {}),
|
|
21
|
-
}) });
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
if (!id)
|
|
25
|
-
throw new AgentError('not_found', 'Route not found.');
|
|
26
|
-
if (!child && context.method === 'GET') {
|
|
27
|
-
const session = context.agents.getSession(id);
|
|
28
|
-
if (!session)
|
|
29
|
-
throw new AgentError('not_found', `Session not found: ${id}`);
|
|
30
|
-
context.json(200, { session });
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
if (!child && context.method === 'DELETE') {
|
|
34
|
-
context.json(200, { session: context.agents.archiveSession(id) });
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
if (child === 'events' && context.method === 'GET') {
|
|
38
|
-
const after = Number(context.url.searchParams.get('after') || 0);
|
|
39
|
-
const limit = Number(context.url.searchParams.get('limit') || 200);
|
|
40
|
-
context.json(200, { events: context.agents.listEvents(id, after, limit) });
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
if (child === 'turns' && context.method === 'GET') {
|
|
44
|
-
context.json(200, { turns: context.agents.listTurns(id) });
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
if (child === 'cancel' && context.method === 'POST') {
|
|
48
|
-
context.json(202, { turn: context.agents.cancelSession(id) });
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
if (child === 'messages' && context.method === 'POST') {
|
|
52
|
-
const body = await context.body();
|
|
53
|
-
if (body.message === undefined)
|
|
54
|
-
throw new AgentError('invalid_input', 'message is required.');
|
|
55
|
-
const enqueued = context.agents.enqueue(id, {
|
|
56
|
-
message: body.message,
|
|
57
|
-
...(typeof body.idempotencyKey === 'string' ? { idempotencyKey: body.idempotencyKey } : {}),
|
|
58
|
-
});
|
|
59
|
-
if (body.wait === false) {
|
|
60
|
-
context.json(202, { turn: enqueued.turn, duplicate: enqueued.duplicate });
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
context.json(200, { turn: await enqueued.completion, duplicate: enqueued.duplicate });
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
return false;
|
|
67
|
-
}
|
package/dist/http/stream.d.ts
DELETED
package/dist/http/stream.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { AgentError } from '../errors.js';
|
|
2
|
-
export function streamEvents(agents, sessionId, request, response, after = 0) {
|
|
3
|
-
if (!agents.getSession(sessionId))
|
|
4
|
-
throw new AgentError('not_found', `Session not found: ${sessionId}`);
|
|
5
|
-
response.writeHead(200, {
|
|
6
|
-
'content-type': 'text/event-stream',
|
|
7
|
-
'cache-control': 'no-cache, no-transform',
|
|
8
|
-
connection: 'keep-alive',
|
|
9
|
-
});
|
|
10
|
-
response.flushHeaders();
|
|
11
|
-
let last = after;
|
|
12
|
-
const write = (event) => {
|
|
13
|
-
if (event.sequence <= last || response.destroyed)
|
|
14
|
-
return;
|
|
15
|
-
last = event.sequence;
|
|
16
|
-
response.write(`id: ${event.sequence}\nevent: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
|
|
17
|
-
};
|
|
18
|
-
for (const event of agents.listEvents(sessionId, after, 1000))
|
|
19
|
-
write(event);
|
|
20
|
-
const unsubscribe = agents.subscribe(sessionId, write);
|
|
21
|
-
const heartbeat = setInterval(() => {
|
|
22
|
-
if (!response.destroyed)
|
|
23
|
-
response.write(': heartbeat\n\n');
|
|
24
|
-
}, 15_000);
|
|
25
|
-
heartbeat.unref();
|
|
26
|
-
request.once('close', () => {
|
|
27
|
-
clearInterval(heartbeat);
|
|
28
|
-
unsubscribe();
|
|
29
|
-
});
|
|
30
|
-
}
|