@meistrari/agent-core 0.0.0 → 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/README.md +24 -3
- package/bin/supervisor.ts +116 -0
- package/package.json +42 -3
- package/scripts/build-supervisor-executable.ts +32 -0
- package/src/agents/agent-error-serializer.ts +23 -0
- package/src/agents/agent-event-stream.ts +81 -0
- package/src/agents/agent-id.ts +17 -0
- package/src/agents/agent-operation.ts +11 -0
- package/src/agents/agent-provider.ts +37 -0
- package/src/agents/agent-run.ts +32 -0
- package/src/agents/agent-runtime-error.ts +161 -0
- package/src/agents/agent-session-events.ts +32 -0
- package/src/agents/agent-tool-runner.ts +83 -0
- package/src/agents/agent-tool.ts +111 -0
- package/src/agents/author-context.ts +36 -0
- package/src/agents/claude/claude-command-mapper.ts +234 -0
- package/src/agents/claude/claude-event-mapper.ts +736 -0
- package/src/agents/claude/claude-provider.ts +191 -0
- package/src/agents/claude/claude-run.ts +464 -0
- package/src/agents/claude/claude-tool-mapper.ts +186 -0
- package/src/agents/claude/index.ts +1 -0
- package/src/agents/codex/codex-auth.ts +34 -0
- package/src/agents/codex/codex-command-mapper.ts +78 -0
- package/src/agents/codex/codex-event-mapper.ts +708 -0
- package/src/agents/codex/codex-json-rpc-client.ts +326 -0
- package/src/agents/codex/codex-protocol.ts +36 -0
- package/src/agents/codex/codex-provider.ts +1050 -0
- package/src/agents/codex/codex-run.ts +404 -0
- package/src/agents/codex/codex-skill-catalog.ts +158 -0
- package/src/agents/codex/codex-skill-roots.ts +19 -0
- package/src/agents/codex/codex-tool-mapper.ts +55 -0
- package/src/agents/codex/codex.errors.ts +68 -0
- package/src/agents/codex/generated/meta.gen.ts +606 -0
- package/src/agents/codex/generated/namespaces.gen.ts +311 -0
- package/src/agents/codex/generated/schema.gen.ts +34883 -0
- package/src/agents/codex/index.ts +2 -0
- package/src/agents/index.ts +14 -0
- package/src/agents/input-attachment-preparation.ts +86 -0
- package/src/agents/input-attachment.errors.ts +16 -0
- package/src/agents/instructions.ts +8 -0
- package/src/agents/materialized-input-attachment.ts +26 -0
- package/src/agents/message-id.ts +23 -0
- package/src/agents/normalize.ts +8 -0
- package/src/agents/sandbox-environment.ts +1 -0
- package/src/agents/tools/ping.tool.ts +13 -0
- package/src/agents/user-input-request.ts +470 -0
- package/src/provenance.gen.ts +3 -3
- package/src/supervisor/agent-provider-factory.ts +189 -0
- package/src/supervisor/bootstrap-binder.ts +125 -0
- package/src/supervisor/config.ts +49 -0
- package/src/supervisor/control-authority-verifier.ts +135 -0
- package/src/supervisor/create-supervisor-runtime.ts +25 -0
- package/src/supervisor/errors.ts +24 -0
- package/src/supervisor/index.ts +34 -0
- package/src/supervisor/persistence/json.ts +21 -0
- package/src/supervisor/persistence/state-discovery.ts +56 -0
- package/src/supervisor/persistence/supervisor-store.ts +364 -0
- package/src/supervisor/ports/index.ts +109 -0
- package/src/supervisor/provider-factory.ts +37 -0
- package/src/supervisor/resident.ts +143 -0
- package/src/supervisor/rpc-client.ts +120 -0
- package/src/supervisor/runtime-handler.ts +309 -0
- package/src/supervisor/websocket-server.ts +434 -0
- package/src/supervisor-protocol/bootstrap.ts +1 -1
- package/src/template-onboarding.ts +47 -0
- package/src/testing/es256-test-keys.ts +73 -0
- package/src/testing/in-memory-runtime-control-plane.ts +205 -0
- package/src/testing/index.ts +6 -0
- package/src/testing/loopback-supervisor-connection.ts +71 -0
- package/src/testing/scripted-provider.ts +64 -0
- package/src/worker-runtime-client/command-pump.ts +132 -0
- package/src/worker-runtime-client/connection-attempt.ts +340 -0
- package/src/worker-runtime-client/control-authority-signer.ts +100 -0
- package/src/worker-runtime-client/e2b-supervisor-connection.ts +102 -0
- package/src/worker-runtime-client/frame-processor.ts +178 -0
- package/src/worker-runtime-client/index.ts +27 -0
- package/src/worker-runtime-client/lease-reconciler.ts +14 -0
- package/src/worker-runtime-client/ports.ts +137 -0
- package/src/worker-runtime-client/postgres-notification-listener.ts +91 -0
- package/src/worker-runtime-client/rpc-dispatcher.ts +27 -0
- package/src/worker-runtime-client/rpc-request-manager.ts +141 -0
- package/src/worker-runtime-client/sandbox-connection-runtime.ts +300 -0
- package/src/worker-runtime-client/token-crypto.ts +46 -0
package/README.md
CHANGED
|
@@ -7,12 +7,20 @@ Shared contracts and runtime modules for Tela coding-agent sandboxes.
|
|
|
7
7
|
| `@meistrari/agent-core/protocol` | Provider-neutral agent commands, events, content, tools, usage, work items (Claude and Codex share it). |
|
|
8
8
|
| `@meistrari/agent-core/supervisor-protocol/*` | Strict wire contracts between a worker runtime and the resident sandbox supervisor: bootstrap, commands, events, reverse RPC, control authority. |
|
|
9
9
|
| `@meistrari/agent-core/errors`, `/logger`, `/provenance` | Shared primitives. |
|
|
10
|
-
| `@meistrari/agent-core/supervisor` | Sandbox-resident supervisor runtime (Bun, SQLite). |
|
|
11
|
-
| `@meistrari/agent-core/worker-runtime-client` | Control-plane WSS client: leases, generation fencing, command pump, durable event
|
|
12
|
-
| `@meistrari/agent-core/
|
|
10
|
+
| `@meistrari/agent-core/supervisor` | Sandbox-resident supervisor runtime (Bun, WebSocket, SQLite), provider factory, and product-facing ports. |
|
|
11
|
+
| `@meistrari/agent-core/worker-runtime-client` | Control-plane WSS client: leases, generation fencing, command pump, durable event ACKs, reverse RPC dispatch, ES256 authority, token crypto, and PG notifications. |
|
|
12
|
+
| `@meistrari/agent-core/testing` | In-memory control-plane, scripted provider, loopback connection, and ES256 test fixtures. |
|
|
13
|
+
| `@meistrari/agent-core/template-onboarding` | Validated service-owned E2B template registration and immutable provenance contract. |
|
|
14
|
+
| `@meistrari/agent-core/agents` | Provider-neutral agent, run, tool, event-stream, and instruction-composer contracts. |
|
|
15
|
+
| `@meistrari/agent-core/claude` | Claude Agent SDK provider with normalized lifecycle, message, tool, work, usage, and user-input events. |
|
|
16
|
+
| `@meistrari/agent-core/codex` | Codex app-server provider, JSON-RPC transport, normalized event mapping, and pluggable authentication. |
|
|
17
|
+
|
|
18
|
+
The supervisor accepts the Claude and Codex adapters through `createAgentSupervisorProviderFactory`, preserving the same durable wire protocol for either harness.
|
|
13
19
|
|
|
14
20
|
The package ships TypeScript source and targets Bun `>=1.3.10`. There is no wire protocol version: a sandbox keeps the exact agent-core build it was created with for its lifetime (immutable sandbox invariant). Incompatible wire changes require reprovisioning sandboxes, never in-place upgrades.
|
|
15
21
|
|
|
22
|
+
agent-core does not own a universal E2B template. Each consuming service builds and onboards its own template against the shared contracts and records that template's agent-core provenance. agent-api and Remy may therefore use different base images, harness versions, system packages, and startup hooks without forking the control protocol.
|
|
23
|
+
|
|
16
24
|
## Development
|
|
17
25
|
|
|
18
26
|
```bash
|
|
@@ -21,6 +29,19 @@ bun run lint
|
|
|
21
29
|
bun run typecheck
|
|
22
30
|
bun run test # unit tests (src/**/*.test.ts)
|
|
23
31
|
bun run test:e2e # loopback worker <-> supervisor e2e
|
|
32
|
+
bun run build:supervisor # cross-compile the Linux x64 resident executable
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Resident supervisor entry
|
|
36
|
+
|
|
37
|
+
`agent-core-supervisor` loads a product-owned extensions module so credentials, tools, workspace preparation, hooks, payload storage, and provider construction remain outside the shared runtime:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bunx agent-core-supervisor \
|
|
41
|
+
--extensions ./supervisor-extensions.ts \
|
|
42
|
+
--authority-jwks-path ./control-authority.json
|
|
24
43
|
```
|
|
25
44
|
|
|
45
|
+
Use `--loopback-tcp` for local and image-build probes. Production images default to the protected Unix socket. The extension module exports `default`, `extensions`, or `createSupervisorExtensions()` matching `SupervisorExtensions`.
|
|
46
|
+
|
|
26
47
|
Publishing runs from `main` through `.github/workflows/publish.yml` (conventional commits decide the bump).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import type { SupervisorExtensions } from '../src/supervisor/create-supervisor-runtime'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import { createLogger } from '../src/logger'
|
|
6
|
+
import {
|
|
7
|
+
createSupervisorRuntime,
|
|
8
|
+
residentSupervisorConfig,
|
|
9
|
+
TERMINAL_ERROR_CODE,
|
|
10
|
+
} from '../src/supervisor'
|
|
11
|
+
|
|
12
|
+
interface SupervisorExtensionsModule {
|
|
13
|
+
default?: SupervisorExtensions
|
|
14
|
+
extensions?: SupervisorExtensions
|
|
15
|
+
createSupervisorExtensions?: (input: {
|
|
16
|
+
developmentFaultDirectory?: string
|
|
17
|
+
}) => SupervisorExtensions | Promise<SupervisorExtensions>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const arguments_ = process.argv.slice(2)
|
|
21
|
+
if (arguments_.includes('--help')) {
|
|
22
|
+
process.stdout.write(`agent-core-supervisor
|
|
23
|
+
|
|
24
|
+
Required:
|
|
25
|
+
--extensions <path> Supervisor extensions module (or AGENT_CORE_SUPERVISOR_EXTENSIONS)
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
--loopback-tcp Listen on 127.0.0.1:8080 instead of the protected Unix socket
|
|
29
|
+
--port <number> Override the loopback TCP port
|
|
30
|
+
--authority-jwks-path <path> Override the ES256 public JWKS configuration
|
|
31
|
+
--sandbox-id-path <path> Override the local E2B sandbox identity file
|
|
32
|
+
--state-root <path> Override the supervisor SQLite state root
|
|
33
|
+
--development-fault-directory <path>
|
|
34
|
+
--help
|
|
35
|
+
`)
|
|
36
|
+
process.exit(0)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await main()
|
|
40
|
+
|
|
41
|
+
async function main(): Promise<void> {
|
|
42
|
+
const logger = createLogger({ base: { runtime: 'agent-core-supervisor' } })
|
|
43
|
+
try {
|
|
44
|
+
const developmentFaultDirectory = option('--development-fault-directory')
|
|
45
|
+
const extensions = await loadExtensions({
|
|
46
|
+
path: option('--extensions') ?? process.env.AGENT_CORE_SUPERVISOR_EXTENSIONS,
|
|
47
|
+
developmentFaultDirectory,
|
|
48
|
+
})
|
|
49
|
+
const portValue = option('--port')
|
|
50
|
+
const port = portValue === undefined ? undefined : parsePort(portValue)
|
|
51
|
+
const runtime = await createSupervisorRuntime({
|
|
52
|
+
config: residentSupervisorConfig({
|
|
53
|
+
listen: arguments_.includes('--loopback-tcp') ? 'loopback-tcp' : 'protected-unix',
|
|
54
|
+
...(port === undefined ? {} : { port }),
|
|
55
|
+
...(option('--authority-jwks-path') ? { authorityJwksPath: option('--authority-jwks-path') } : {}),
|
|
56
|
+
...(option('--sandbox-id-path') ? { sandboxIdPath: option('--sandbox-id-path') } : {}),
|
|
57
|
+
...(option('--state-root') ? { stateRoot: option('--state-root') } : {}),
|
|
58
|
+
}),
|
|
59
|
+
extensions,
|
|
60
|
+
logger,
|
|
61
|
+
})
|
|
62
|
+
let stopping: Promise<void> | undefined
|
|
63
|
+
const stop = (reason: 'sigterm' | 'sigint') => {
|
|
64
|
+
stopping ??= runtime.stop({ reason }).then(() => {
|
|
65
|
+
process.exitCode = 0
|
|
66
|
+
})
|
|
67
|
+
return stopping
|
|
68
|
+
}
|
|
69
|
+
process.once('SIGTERM', () => void stop('sigterm'))
|
|
70
|
+
process.once('SIGINT', () => void stop('sigint'))
|
|
71
|
+
runtime.start()
|
|
72
|
+
logger.info({
|
|
73
|
+
transport: arguments_.includes('--loopback-tcp') ? 'loopback-tcp' : 'protected-unix',
|
|
74
|
+
port: runtime.port(),
|
|
75
|
+
developmentFaults: developmentFaultDirectory ? 'enabled' : 'disabled',
|
|
76
|
+
}, 'resident supervisor is listening')
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
logger.error({ err: error }, 'resident supervisor failed to start')
|
|
80
|
+
process.exitCode = TERMINAL_ERROR_CODE
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function loadExtensions(input: {
|
|
85
|
+
path: string | undefined
|
|
86
|
+
developmentFaultDirectory: string | undefined
|
|
87
|
+
}): Promise<SupervisorExtensions> {
|
|
88
|
+
if (!input.path)
|
|
89
|
+
throw new Error('--extensions or AGENT_CORE_SUPERVISOR_EXTENSIONS is required.')
|
|
90
|
+
const module = await import(pathToFileURL(resolve(input.path)).href) as SupervisorExtensionsModule
|
|
91
|
+
const extensions = module.createSupervisorExtensions
|
|
92
|
+
? await module.createSupervisorExtensions({
|
|
93
|
+
...(input.developmentFaultDirectory ? { developmentFaultDirectory: input.developmentFaultDirectory } : {}),
|
|
94
|
+
})
|
|
95
|
+
: module.default ?? module.extensions
|
|
96
|
+
if (!extensions)
|
|
97
|
+
throw new Error('Supervisor extensions module must export default, extensions, or createSupervisorExtensions().')
|
|
98
|
+
return extensions
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function option(name: string): string | undefined {
|
|
102
|
+
const index = arguments_.indexOf(name)
|
|
103
|
+
if (index < 0)
|
|
104
|
+
return undefined
|
|
105
|
+
const value = arguments_[index + 1]
|
|
106
|
+
if (!value || value.startsWith('--'))
|
|
107
|
+
throw new Error(`${name} requires a value.`)
|
|
108
|
+
return value
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parsePort(value: string): number {
|
|
112
|
+
const port = Number(value)
|
|
113
|
+
if (!Number.isSafeInteger(port) || port < 0 || port > 65_535)
|
|
114
|
+
throw new Error('--port must be an integer between 0 and 65535.')
|
|
115
|
+
return port
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meistrari/agent-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"packageManager": "bun@1.3.12",
|
|
6
6
|
"description": "Shared contracts and runtime modules for Tela coding-agent sandboxes: agent protocol, supervisor wire protocol, resident supervisor, worker runtime client, and Claude/Codex harness adapters.",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/meistrari/agent-core.git"
|
|
10
|
+
"url": "git+https://github.com/meistrari/agent-core.git"
|
|
11
11
|
},
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"./protocol/agent-content": "./src/protocol/agent-content.ts",
|
|
23
23
|
"./protocol/agent-tool-name": "./src/protocol/agent-tool-name.ts",
|
|
24
24
|
"./protocol/agent-work": "./src/protocol/agent-work.ts",
|
|
25
|
+
"./agents": "./src/agents/index.ts",
|
|
26
|
+
"./claude": "./src/agents/claude/index.ts",
|
|
27
|
+
"./codex": "./src/agents/codex/index.ts",
|
|
25
28
|
"./supervisor-protocol/agent-event-wrapper": "./src/supervisor-protocol/agent-event-wrapper.ts",
|
|
26
29
|
"./supervisor-protocol/bootstrap": "./src/supervisor-protocol/bootstrap.ts",
|
|
27
30
|
"./supervisor-protocol/bootstrap-rejection-receipt": "./src/supervisor-protocol/bootstrap-rejection-receipt.ts",
|
|
@@ -42,7 +45,15 @@
|
|
|
42
45
|
"./supervisor-protocol/supervisor-agent-run-snapshot": "./src/supervisor-protocol/supervisor-agent-run-snapshot.ts",
|
|
43
46
|
"./supervisor-protocol/supervisor-event": "./src/supervisor-protocol/supervisor-event.ts",
|
|
44
47
|
"./supervisor-protocol/tela-page-content": "./src/supervisor-protocol/tela-page-content.ts",
|
|
45
|
-
"./supervisor-protocol/wire-codec": "./src/supervisor-protocol/wire-codec.ts"
|
|
48
|
+
"./supervisor-protocol/wire-codec": "./src/supervisor-protocol/wire-codec.ts",
|
|
49
|
+
"./template-onboarding": "./src/template-onboarding.ts",
|
|
50
|
+
"./supervisor": "./src/supervisor/index.ts",
|
|
51
|
+
"./supervisor/provider-factory": "./src/supervisor/provider-factory.ts",
|
|
52
|
+
"./testing": "./src/testing/index.ts",
|
|
53
|
+
"./worker-runtime-client": "./src/worker-runtime-client/index.ts"
|
|
54
|
+
},
|
|
55
|
+
"bin": {
|
|
56
|
+
"agent-core-supervisor": "bin/supervisor.ts"
|
|
46
57
|
},
|
|
47
58
|
"engines": {
|
|
48
59
|
"bun": ">=1.3.10"
|
|
@@ -53,17 +64,45 @@
|
|
|
53
64
|
"typecheck": "tsc --noEmit",
|
|
54
65
|
"test": "bun test src/",
|
|
55
66
|
"test:e2e": "bun test --pass-with-no-tests e2e/",
|
|
67
|
+
"build:supervisor": "bun scripts/build-supervisor-executable.ts",
|
|
56
68
|
"provenance": "bun scripts/write-provenance.ts",
|
|
57
69
|
"build": "bun scripts/write-provenance.ts"
|
|
58
70
|
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"@anthropic-ai/claude-agent-sdk": ">=0.3.207 <0.4.0",
|
|
73
|
+
"@anthropic-ai/sdk": "^0.93.0",
|
|
74
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
75
|
+
"pg": "^8.16.3"
|
|
76
|
+
},
|
|
77
|
+
"peerDependenciesMeta": {
|
|
78
|
+
"@anthropic-ai/claude-agent-sdk": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"@anthropic-ai/sdk": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@modelcontextprotocol/sdk": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"pg": {
|
|
88
|
+
"optional": true
|
|
89
|
+
}
|
|
90
|
+
},
|
|
59
91
|
"dependencies": {
|
|
92
|
+
"jose": "^6.1.3",
|
|
60
93
|
"pino": "^9.7.0",
|
|
94
|
+
"ulid": "^3.0.1",
|
|
61
95
|
"zod": "^4.1.12"
|
|
62
96
|
},
|
|
63
97
|
"devDependencies": {
|
|
64
98
|
"@antfu/eslint-config": "4.16.2",
|
|
99
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.224",
|
|
100
|
+
"@anthropic-ai/sdk": "0.93.0",
|
|
101
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
65
102
|
"@types/bun": "1.3.12",
|
|
103
|
+
"@types/pg": "^8.15.5",
|
|
66
104
|
"eslint": "^9.30.0",
|
|
105
|
+
"pg": "^8.16.3",
|
|
67
106
|
"typescript": "5.9.3"
|
|
68
107
|
}
|
|
69
108
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises'
|
|
2
|
+
import { dirname, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
const outputArgument = process.argv.indexOf('--outfile')
|
|
5
|
+
const output = resolve(outputArgument >= 0
|
|
6
|
+
? requiredArgument('--outfile', outputArgument)
|
|
7
|
+
: 'dist/agent-core-supervisor-linux-x64')
|
|
8
|
+
const targetArgument = process.argv.indexOf('--target')
|
|
9
|
+
const target = targetArgument >= 0
|
|
10
|
+
? requiredArgument('--target', targetArgument) as Bun.Build.CompileTarget
|
|
11
|
+
: 'bun-linux-x64'
|
|
12
|
+
|
|
13
|
+
await mkdir(dirname(output), { recursive: true })
|
|
14
|
+
const result = await Bun.build({
|
|
15
|
+
entrypoints: [resolve('bin/supervisor.ts')],
|
|
16
|
+
compile: { target, outfile: output },
|
|
17
|
+
minify: true,
|
|
18
|
+
sourcemap: 'none',
|
|
19
|
+
})
|
|
20
|
+
if (!result.success) {
|
|
21
|
+
for (const log of result.logs)
|
|
22
|
+
console.error(log)
|
|
23
|
+
process.exit(1)
|
|
24
|
+
}
|
|
25
|
+
console.log(output)
|
|
26
|
+
|
|
27
|
+
function requiredArgument(name: string, index: number): string {
|
|
28
|
+
const value = process.argv[index + 1]
|
|
29
|
+
if (!value || value.startsWith('--'))
|
|
30
|
+
throw new Error(`${name} requires a value.`)
|
|
31
|
+
return value
|
|
32
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AgentError } from '../protocol'
|
|
2
|
+
|
|
3
|
+
type ErrorWithCode = Error & { code?: unknown, cause?: unknown }
|
|
4
|
+
|
|
5
|
+
const MAX_CAUSE_DEPTH = 3
|
|
6
|
+
|
|
7
|
+
export function serializeAgentError(error: unknown, depth = 0): AgentError {
|
|
8
|
+
if (error instanceof Error) {
|
|
9
|
+
const withCode = error as ErrorWithCode
|
|
10
|
+
const serialized: AgentError = { message: error.message || error.name || 'Unknown error' }
|
|
11
|
+
if (typeof withCode.code === 'string' && withCode.code.trim())
|
|
12
|
+
serialized.code = withCode.code
|
|
13
|
+
if (error.name && error.name !== 'Error')
|
|
14
|
+
serialized.name = error.name
|
|
15
|
+
if (error.stack)
|
|
16
|
+
serialized.stack = error.stack
|
|
17
|
+
if (withCode.cause !== undefined && depth < MAX_CAUSE_DEPTH)
|
|
18
|
+
serialized.cause = serializeAgentError(withCode.cause, depth + 1)
|
|
19
|
+
return serialized
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { message: String(error) }
|
|
23
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AgentEvent, AgentProviderId } from '../protocol'
|
|
2
|
+
import { agentEventSchema, InvalidAgentEventError } from '../protocol'
|
|
3
|
+
import { createAgentEventId } from './agent-id'
|
|
4
|
+
|
|
5
|
+
type AgentEventEnvelopeField = 'eventId' | 'provider' | 'sessionId' | 'providerSessionId' | 'timestamp' | 'sequence'
|
|
6
|
+
export type AgentEventDraft = AgentEvent extends infer Event
|
|
7
|
+
? Event extends AgentEvent
|
|
8
|
+
? Omit<Event, AgentEventEnvelopeField>
|
|
9
|
+
: never
|
|
10
|
+
: never
|
|
11
|
+
|
|
12
|
+
export class AgentEventStream implements AsyncIterable<AgentEvent> {
|
|
13
|
+
private readonly items: AgentEvent[] = []
|
|
14
|
+
private readonly resolvers: Array<(result: IteratorResult<AgentEvent>) => void> = []
|
|
15
|
+
private ended = false
|
|
16
|
+
private sequence = 0
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly envelope: {
|
|
20
|
+
provider: AgentProviderId
|
|
21
|
+
sessionId: string
|
|
22
|
+
providerSessionId: string
|
|
23
|
+
},
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
push(draft: AgentEventDraft): AgentEvent {
|
|
27
|
+
if (this.ended) {
|
|
28
|
+
throw new InvalidAgentEventError({ message: 'Cannot push an event after the stream has ended.' })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const parsed = agentEventSchema.safeParse({
|
|
32
|
+
...draft,
|
|
33
|
+
eventId: createAgentEventId(),
|
|
34
|
+
provider: this.envelope.provider,
|
|
35
|
+
sessionId: this.envelope.sessionId,
|
|
36
|
+
providerSessionId: this.envelope.providerSessionId,
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
sequence: this.sequence,
|
|
39
|
+
})
|
|
40
|
+
if (!parsed.success) {
|
|
41
|
+
throw new InvalidAgentEventError({
|
|
42
|
+
message: 'Agent event draft is invalid.',
|
|
43
|
+
cause: parsed.error,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
const event = parsed.data
|
|
47
|
+
this.sequence += 1
|
|
48
|
+
|
|
49
|
+
const resolver = this.resolvers.shift()
|
|
50
|
+
if (resolver) {
|
|
51
|
+
resolver({ value: event, done: false })
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.items.push(event)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return event
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
end(): void {
|
|
61
|
+
if (this.ended)
|
|
62
|
+
return
|
|
63
|
+
this.ended = true
|
|
64
|
+
for (const resolver of this.resolvers.splice(0)) {
|
|
65
|
+
resolver({ value: undefined, done: true })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
[Symbol.asyncIterator](): AsyncIterator<AgentEvent> {
|
|
70
|
+
return {
|
|
71
|
+
next: async () => {
|
|
72
|
+
const item = this.items.shift()
|
|
73
|
+
if (item)
|
|
74
|
+
return await Promise.resolve({ value: item, done: false })
|
|
75
|
+
if (this.ended)
|
|
76
|
+
return await Promise.resolve({ value: undefined, done: true })
|
|
77
|
+
return await new Promise<IteratorResult<AgentEvent>>(resolve => this.resolvers.push(resolve))
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ulid } from 'ulid'
|
|
2
|
+
|
|
3
|
+
export function createAgentEventId(): string {
|
|
4
|
+
return `evt_${ulid()}`
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createUserInputRequestId(): string {
|
|
8
|
+
return `req_${ulid()}`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createRuntimeTurnId(): string {
|
|
12
|
+
return `turn_${ulid()}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createRuntimeWorkItemId(): string {
|
|
16
|
+
return `work_${ulid()}`
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentInputAttachmentPreparation } from './materialized-input-attachment'
|
|
2
|
+
|
|
3
|
+
export type AgentSenderContext
|
|
4
|
+
= | { kind: 'application' }
|
|
5
|
+
| { kind: 'slack', mention: string }
|
|
6
|
+
|
|
7
|
+
export interface AgentOperationOptions {
|
|
8
|
+
signal?: AbortSignal
|
|
9
|
+
inputAttachmentPreparation?: AgentInputAttachmentPreparation
|
|
10
|
+
senderContext?: AgentSenderContext
|
|
11
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentModelId,
|
|
3
|
+
AgentProviderId,
|
|
4
|
+
AgentReasoningEffort,
|
|
5
|
+
AgentToolDefinition,
|
|
6
|
+
} from '../protocol'
|
|
7
|
+
import type { AgentOperationOptions } from './agent-operation'
|
|
8
|
+
import type { AgentRun } from './agent-run'
|
|
9
|
+
|
|
10
|
+
interface AgentSessionConfigInput {
|
|
11
|
+
sessionId: string
|
|
12
|
+
cwd: string
|
|
13
|
+
workspaceRepositoryRoots: readonly string[]
|
|
14
|
+
reasoningEffort?: AgentReasoningEffort
|
|
15
|
+
tools?: AgentToolDefinition[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Distributive over the provider so each arm keeps its provider correlated with its own model union:
|
|
19
|
+
// the default `AgentProviderId` reproduces the `codex | claude` discriminated union, while a single
|
|
20
|
+
// provider narrows to exactly that arm.
|
|
21
|
+
export type AgentSessionOpenInput<Provider extends AgentProviderId = AgentProviderId>
|
|
22
|
+
= Provider extends AgentProviderId
|
|
23
|
+
? AgentSessionConfigInput & { provider: Provider, model?: AgentModelId<Provider> }
|
|
24
|
+
: never
|
|
25
|
+
|
|
26
|
+
export type AgentSessionResumeInput<Provider extends AgentProviderId = AgentProviderId>
|
|
27
|
+
= Provider extends AgentProviderId
|
|
28
|
+
? AgentSessionConfigInput & { provider: Provider, providerSessionId: string, model?: AgentModelId<Provider> }
|
|
29
|
+
: never
|
|
30
|
+
|
|
31
|
+
// Lifecycle only: openSession/resumeSession never apply user input. Callers submit the durable
|
|
32
|
+
// command through the returned run afterwards, so the provider binding can be persisted first.
|
|
33
|
+
export interface AgentProvider<Provider extends AgentProviderId = AgentProviderId> {
|
|
34
|
+
readonly id: Provider
|
|
35
|
+
openSession: (input: AgentSessionOpenInput<Provider>, options?: AgentOperationOptions) => Promise<AgentRun>
|
|
36
|
+
resumeSession: (input: AgentSessionResumeInput<Provider>, options?: AgentOperationOptions) => Promise<AgentRun>
|
|
37
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentEvent,
|
|
3
|
+
AgentRunMetadata,
|
|
4
|
+
InterruptAgentCommand,
|
|
5
|
+
RespondUserInputAgentCommand,
|
|
6
|
+
SendPromptCommand,
|
|
7
|
+
StopAgentCommand,
|
|
8
|
+
} from '../protocol'
|
|
9
|
+
import type { AgentOperationOptions } from './agent-operation'
|
|
10
|
+
|
|
11
|
+
// Whether respond-user-input submitted provider input. `already_delivered` is the durable-store
|
|
12
|
+
// idempotence fact for a redriven duplicate, not a provider prompt-acceptance receipt.
|
|
13
|
+
export type AgentUserInputSubmission = 'submitted' | 'already_delivered'
|
|
14
|
+
|
|
15
|
+
// Positive prompt-submission receipt. `turn` carries the provider's real turn identity (Codex: nested
|
|
16
|
+
// `turn.id` from turn/start, flat `turnId` from turn/steer). `submitted` is the stream-submission arm
|
|
17
|
+
// for providers whose input boundary carries no turn identity.
|
|
18
|
+
export type AgentPromptAcceptance
|
|
19
|
+
= | { kind: 'turn', turnId: string, placement: 'started' | 'steered' }
|
|
20
|
+
| { kind: 'submitted' }
|
|
21
|
+
|
|
22
|
+
export interface AgentRun {
|
|
23
|
+
readonly metadata: AgentRunMetadata
|
|
24
|
+
readonly events: AsyncIterable<AgentEvent>
|
|
25
|
+
prepareWorkspace: (options?: AgentOperationOptions) => Promise<void>
|
|
26
|
+
// Providers without root registration accept the root without provider I/O.
|
|
27
|
+
addWorkspaceRepositoryRoot: (root: string, options?: AgentOperationOptions) => Promise<void>
|
|
28
|
+
sendPrompt: (command: SendPromptCommand, options?: AgentOperationOptions) => Promise<AgentPromptAcceptance>
|
|
29
|
+
respondUserInput: (command: RespondUserInputAgentCommand, options?: AgentOperationOptions) => Promise<AgentUserInputSubmission>
|
|
30
|
+
interrupt: (command: InterruptAgentCommand, options?: AgentOperationOptions) => Promise<void>
|
|
31
|
+
stop: (command: StopAgentCommand, options?: AgentOperationOptions) => Promise<void>
|
|
32
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { InfrastructureErrorInput, ProviderErrorInput, ValidationErrorInput } from '../errors'
|
|
2
|
+
import { InfrastructureError, ProviderError, ValidationError } from '../errors'
|
|
3
|
+
|
|
4
|
+
type RuntimeProviderErrorInput = Omit<ProviderErrorInput, 'code'> & { code?: string }
|
|
5
|
+
type RuntimeValidationErrorInput = Omit<ValidationErrorInput, 'code'> & { code?: string }
|
|
6
|
+
type RuntimeInfrastructureErrorInput = Omit<InfrastructureErrorInput, 'code'> & { code?: string }
|
|
7
|
+
|
|
8
|
+
export class AgentProviderRuntimeError extends ProviderError {
|
|
9
|
+
constructor(input: RuntimeProviderErrorInput) {
|
|
10
|
+
const { code = 'agents.provider-error', ...rest } = input
|
|
11
|
+
super({ code, ...rest })
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// The one recovery consequence the supervisor loop performs for an input/operation the boundary
|
|
16
|
+
// proved was not accepted by the provider or coordinator.
|
|
17
|
+
export type KnownNotAcceptedRecovery
|
|
18
|
+
= | { kind: 'retry', reason: string, retryAfterMs?: number }
|
|
19
|
+
| { kind: 'external_action', reason: string }
|
|
20
|
+
| { kind: 'terminal', reason: string }
|
|
21
|
+
|
|
22
|
+
export class AgentNotAcceptedError extends AgentProviderRuntimeError {
|
|
23
|
+
readonly recovery: KnownNotAcceptedRecovery
|
|
24
|
+
|
|
25
|
+
constructor(input: Omit<RuntimeProviderErrorInput, 'code' | 'retryable'> & { recovery: KnownNotAcceptedRecovery }) {
|
|
26
|
+
const { recovery, ...rest } = input
|
|
27
|
+
super({
|
|
28
|
+
code: 'agents.input-not-accepted',
|
|
29
|
+
publicMessage: 'Agent provider did not accept the operation.',
|
|
30
|
+
retryable: recovery.kind === 'retry',
|
|
31
|
+
...rest,
|
|
32
|
+
})
|
|
33
|
+
this.recovery = recovery
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Turn-boundary race fact: the prompt was proven not accepted. Two producers exist — an adapter
|
|
38
|
+
// pre-defers a `next` while its state shows an active turn (no request issued), and a `turn/steer`
|
|
39
|
+
// answered -32600 is a post-write provider response proving the input was not accepted. Consumed
|
|
40
|
+
// by the supervisor loop, which returns the durable command to pending, stops the drain pass, and
|
|
41
|
+
// re-dispatches on the next work-relevant session-state change — no retry timer, no backoff.
|
|
42
|
+
export class AgentPromptDeferredError extends AgentProviderRuntimeError {
|
|
43
|
+
constructor(input: Omit<RuntimeProviderErrorInput, 'code' | 'retryable'>) {
|
|
44
|
+
super({
|
|
45
|
+
code: 'agents.prompt-deferred',
|
|
46
|
+
publicMessage: 'Agent provider deferred the prompt at a turn boundary race.',
|
|
47
|
+
retryable: false,
|
|
48
|
+
...input,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Transport/response loss after the input may have been written: a delivery-unknown invalidates the
|
|
54
|
+
// run's ownership but does not prove the run has closed (a Codex stdin write failure need not set
|
|
55
|
+
// `stopped`). The supervisor explicitly relinquishes — stops the run and drains its consumer — then
|
|
56
|
+
// durably waits before redriving the same command id; a duplicate is the accepted at-least-once cost.
|
|
57
|
+
export class AgentDeliveryUnknownError extends AgentProviderRuntimeError {
|
|
58
|
+
constructor(input: Omit<RuntimeProviderErrorInput, 'code' | 'retryable'>) {
|
|
59
|
+
super({
|
|
60
|
+
code: 'agents.delivery-unknown',
|
|
61
|
+
publicMessage: 'Agent provider delivery outcome is unknown.',
|
|
62
|
+
retryable: false,
|
|
63
|
+
...input,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class AgentProviderLaunchError extends AgentProviderRuntimeError {
|
|
69
|
+
constructor(input: Omit<RuntimeProviderErrorInput, 'code'>) {
|
|
70
|
+
super({
|
|
71
|
+
code: 'agents.provider-launch-failed',
|
|
72
|
+
publicMessage: 'Agent provider failed to start.',
|
|
73
|
+
retryable: true,
|
|
74
|
+
...input,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class AgentProviderProtocolError extends AgentProviderRuntimeError {
|
|
80
|
+
constructor(input: Omit<RuntimeProviderErrorInput, 'code'>) {
|
|
81
|
+
super({
|
|
82
|
+
code: 'agents.provider-protocol-error',
|
|
83
|
+
publicMessage: 'Agent provider returned an invalid response.',
|
|
84
|
+
retryable: false,
|
|
85
|
+
...input,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class AgentProviderMetadataError extends AgentProviderRuntimeError {
|
|
91
|
+
constructor(input: Omit<RuntimeProviderErrorInput, 'code'>) {
|
|
92
|
+
super({
|
|
93
|
+
code: 'agents.provider-metadata-missing',
|
|
94
|
+
publicMessage: 'Agent provider did not return required session metadata.',
|
|
95
|
+
retryable: false,
|
|
96
|
+
...input,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class AgentRuntimeValidationError extends ValidationError {
|
|
102
|
+
constructor(input: RuntimeValidationErrorInput) {
|
|
103
|
+
const { code = 'agents.validation-failed', ...rest } = input
|
|
104
|
+
super({ code, ...rest })
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class AgentUnsupportedPromptBlockError extends AgentRuntimeValidationError {
|
|
109
|
+
constructor(input: { provider: string, blockType: string }) {
|
|
110
|
+
super({
|
|
111
|
+
code: 'agents.unsupported-prompt-block',
|
|
112
|
+
message: `Provider ${input.provider} does not support prompt block type ${input.blockType}.`,
|
|
113
|
+
publicMessage: 'Agent prompt contains unsupported content for the selected provider.',
|
|
114
|
+
details: input,
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export class AgentToolValidationError extends AgentRuntimeValidationError {
|
|
120
|
+
constructor(input: Omit<RuntimeValidationErrorInput, 'code'>) {
|
|
121
|
+
super({
|
|
122
|
+
code: 'agents.tool-validation-failed',
|
|
123
|
+
publicMessage: 'Agent tool configuration is invalid.',
|
|
124
|
+
...input,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export class AgentRunStateError extends AgentRuntimeValidationError {
|
|
130
|
+
constructor(input: Omit<RuntimeValidationErrorInput, 'code'>) {
|
|
131
|
+
super({
|
|
132
|
+
code: 'agents.invalid-run-state',
|
|
133
|
+
publicMessage: 'Agent run is not in the required state for this operation.',
|
|
134
|
+
...input,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export class AgentUserInputRequestStoreError extends InfrastructureError {
|
|
140
|
+
constructor(input: Omit<RuntimeInfrastructureErrorInput, 'code'>) {
|
|
141
|
+
super({
|
|
142
|
+
code: 'agents.user-input-request-store-error',
|
|
143
|
+
publicMessage: 'Agent user-input request state is unavailable.',
|
|
144
|
+
retryable: false,
|
|
145
|
+
...input,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export class AgentOperationAbortedError extends InfrastructureError {
|
|
151
|
+
constructor(input: Omit<RuntimeInfrastructureErrorInput, 'code' | 'message'> & { message?: string } = {}) {
|
|
152
|
+
super({
|
|
153
|
+
code: 'agents.operation-aborted',
|
|
154
|
+
message: input.message ?? 'Agent operation was aborted.',
|
|
155
|
+
publicMessage: 'Agent operation was aborted.',
|
|
156
|
+
retryable: false,
|
|
157
|
+
details: input.details,
|
|
158
|
+
cause: input.cause,
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
}
|