@meistrari/agent-core 0.0.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 +26 -0
- package/package.json +69 -0
- package/scripts/write-provenance.ts +74 -0
- package/src/errors/app-error.ts +66 -0
- package/src/errors/application-error.ts +55 -0
- package/src/errors/index.ts +16 -0
- package/src/errors/infrastructure-error.ts +13 -0
- package/src/errors/provider-error.ts +13 -0
- package/src/errors/validation-error.ts +19 -0
- package/src/logger/index.ts +75 -0
- package/src/protocol/agent-command.ts +65 -0
- package/src/protocol/agent-content.ts +73 -0
- package/src/protocol/agent-error.ts +81 -0
- package/src/protocol/agent-event.ts +391 -0
- package/src/protocol/agent-json.ts +30 -0
- package/src/protocol/agent-metadata.ts +30 -0
- package/src/protocol/agent-model.ts +68 -0
- package/src/protocol/agent-overflow.ts +18 -0
- package/src/protocol/agent-presentation.ts +98 -0
- package/src/protocol/agent-provider.ts +4 -0
- package/src/protocol/agent-tool-name.ts +48 -0
- package/src/protocol/agent-tool.ts +40 -0
- package/src/protocol/agent-usage.ts +14 -0
- package/src/protocol/agent-user-input.ts +38 -0
- package/src/protocol/agent-work.ts +100 -0
- package/src/protocol/index.ts +13 -0
- package/src/provenance.gen.ts +6 -0
- package/src/provenance.ts +27 -0
- package/src/supervisor-protocol/agent-event-wrapper.ts +179 -0
- package/src/supervisor-protocol/bootstrap-rejection-receipt.ts +3 -0
- package/src/supervisor-protocol/bootstrap.ts +126 -0
- package/src/supervisor-protocol/command-body.ts +82 -0
- package/src/supervisor-protocol/context-file-content.ts +90 -0
- package/src/supervisor-protocol/control-authority.ts +88 -0
- package/src/supervisor-protocol/durability.ts +33 -0
- package/src/supervisor-protocol/envelope.ts +58 -0
- package/src/supervisor-protocol/envelopes/common.ts +7 -0
- package/src/supervisor-protocol/envelopes/control-plane-to-supervisor.ts +38 -0
- package/src/supervisor-protocol/envelopes/supervisor-to-control-plane.ts +89 -0
- package/src/supervisor-protocol/event-body.ts +13 -0
- package/src/supervisor-protocol/input-attachment-content.ts +3 -0
- package/src/supervisor-protocol/payload-overflow.ts +15 -0
- package/src/supervisor-protocol/product-event.ts +27 -0
- package/src/supervisor-protocol/profile.ts +22 -0
- package/src/supervisor-protocol/rpc.ts +77 -0
- package/src/supervisor-protocol/supervisor-agent-run-snapshot.ts +12 -0
- package/src/supervisor-protocol/supervisor-event.ts +361 -0
- package/src/supervisor-protocol/tela-page-content.ts +6 -0
- package/src/supervisor-protocol/wire-codec.ts +71 -0
- package/tsconfig.json +24 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @meistrari/agent-core
|
|
2
|
+
|
|
3
|
+
Shared contracts and runtime modules for Tela coding-agent sandboxes.
|
|
4
|
+
|
|
5
|
+
| Subpath | What it is |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `@meistrari/agent-core/protocol` | Provider-neutral agent commands, events, content, tools, usage, work items (Claude and Codex share it). |
|
|
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
|
+
| `@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 acking, reverse RPC dispatch. |
|
|
12
|
+
| `@meistrari/agent-core/claude`, `/codex` | Harness adapters. |
|
|
13
|
+
|
|
14
|
+
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
|
+
|
|
16
|
+
## Development
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
bun install
|
|
20
|
+
bun run lint
|
|
21
|
+
bun run typecheck
|
|
22
|
+
bun run test # unit tests (src/**/*.test.ts)
|
|
23
|
+
bun run test:e2e # loopback worker <-> supervisor e2e
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Publishing runs from `main` through `.github/workflows/publish.yml` (conventional commits decide the bump).
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meistrari/agent-core",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"packageManager": "bun@1.3.12",
|
|
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
|
+
"license": "UNLICENSED",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/meistrari/agent-core.git"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"registry": "https://registry.npmjs.org"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
"./errors": "./src/errors/index.ts",
|
|
18
|
+
"./errors/application-error": "./src/errors/application-error.ts",
|
|
19
|
+
"./logger": "./src/logger/index.ts",
|
|
20
|
+
"./provenance": "./src/provenance.ts",
|
|
21
|
+
"./protocol": "./src/protocol/index.ts",
|
|
22
|
+
"./protocol/agent-content": "./src/protocol/agent-content.ts",
|
|
23
|
+
"./protocol/agent-tool-name": "./src/protocol/agent-tool-name.ts",
|
|
24
|
+
"./protocol/agent-work": "./src/protocol/agent-work.ts",
|
|
25
|
+
"./supervisor-protocol/agent-event-wrapper": "./src/supervisor-protocol/agent-event-wrapper.ts",
|
|
26
|
+
"./supervisor-protocol/bootstrap": "./src/supervisor-protocol/bootstrap.ts",
|
|
27
|
+
"./supervisor-protocol/bootstrap-rejection-receipt": "./src/supervisor-protocol/bootstrap-rejection-receipt.ts",
|
|
28
|
+
"./supervisor-protocol/command-body": "./src/supervisor-protocol/command-body.ts",
|
|
29
|
+
"./supervisor-protocol/control-authority": "./src/supervisor-protocol/control-authority.ts",
|
|
30
|
+
"./supervisor-protocol/context-file-content": "./src/supervisor-protocol/context-file-content.ts",
|
|
31
|
+
"./supervisor-protocol/durability": "./src/supervisor-protocol/durability.ts",
|
|
32
|
+
"./supervisor-protocol/envelope": "./src/supervisor-protocol/envelope.ts",
|
|
33
|
+
"./supervisor-protocol/envelopes/common": "./src/supervisor-protocol/envelopes/common.ts",
|
|
34
|
+
"./supervisor-protocol/envelopes/control-plane-to-supervisor": "./src/supervisor-protocol/envelopes/control-plane-to-supervisor.ts",
|
|
35
|
+
"./supervisor-protocol/envelopes/supervisor-to-control-plane": "./src/supervisor-protocol/envelopes/supervisor-to-control-plane.ts",
|
|
36
|
+
"./supervisor-protocol/event-body": "./src/supervisor-protocol/event-body.ts",
|
|
37
|
+
"./supervisor-protocol/input-attachment-content": "./src/supervisor-protocol/input-attachment-content.ts",
|
|
38
|
+
"./supervisor-protocol/payload-overflow": "./src/supervisor-protocol/payload-overflow.ts",
|
|
39
|
+
"./supervisor-protocol/profile": "./src/supervisor-protocol/profile.ts",
|
|
40
|
+
"./supervisor-protocol/product-event": "./src/supervisor-protocol/product-event.ts",
|
|
41
|
+
"./supervisor-protocol/rpc": "./src/supervisor-protocol/rpc.ts",
|
|
42
|
+
"./supervisor-protocol/supervisor-agent-run-snapshot": "./src/supervisor-protocol/supervisor-agent-run-snapshot.ts",
|
|
43
|
+
"./supervisor-protocol/supervisor-event": "./src/supervisor-protocol/supervisor-event.ts",
|
|
44
|
+
"./supervisor-protocol/tela-page-content": "./src/supervisor-protocol/tela-page-content.ts",
|
|
45
|
+
"./supervisor-protocol/wire-codec": "./src/supervisor-protocol/wire-codec.ts"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"bun": ">=1.3.10"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"lint:fix": "eslint . --fix",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "bun test src/",
|
|
55
|
+
"test:e2e": "bun test --pass-with-no-tests e2e/",
|
|
56
|
+
"provenance": "bun scripts/write-provenance.ts",
|
|
57
|
+
"build": "bun scripts/write-provenance.ts"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"pino": "^9.7.0",
|
|
61
|
+
"zod": "^4.1.12"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@antfu/eslint-config": "4.16.2",
|
|
65
|
+
"@types/bun": "1.3.12",
|
|
66
|
+
"eslint": "^9.30.0",
|
|
67
|
+
"typescript": "5.9.3"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Writes `src/provenance.gen.ts` with the published version and source sha.
|
|
4
|
+
*
|
|
5
|
+
* The publish workflow runs this before packing so every consumer (the sandbox
|
|
6
|
+
* supervisor, the worker runtime, image builds) can record exactly which
|
|
7
|
+
* agent-core build it embeds. In development the committed placeholder wins.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
|
|
12
|
+
interface Options { version?: string, sha?: string, dryRun: boolean }
|
|
13
|
+
|
|
14
|
+
function parseArguments(argv: readonly string[]): Options {
|
|
15
|
+
const options: Options = { dryRun: false }
|
|
16
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
17
|
+
const argument = argv[index]
|
|
18
|
+
if (argument === '--version') {
|
|
19
|
+
options.version = argv[index + 1]
|
|
20
|
+
index += 1
|
|
21
|
+
}
|
|
22
|
+
else if (argument === '--sha') {
|
|
23
|
+
options.sha = argv[index + 1]
|
|
24
|
+
index += 1
|
|
25
|
+
}
|
|
26
|
+
else if (argument === '--dry-run') {
|
|
27
|
+
options.dryRun = true
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
throw new Error(`Unknown argument: ${argument}`)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return options
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function resolveGitSha(): Promise<string> {
|
|
37
|
+
const process = Bun.spawn(['git', 'rev-parse', 'HEAD'], { stdout: 'pipe', stderr: 'pipe' })
|
|
38
|
+
const output = (await new Response(process.stdout).text()).trim()
|
|
39
|
+
const exitCode = await process.exited
|
|
40
|
+
if (exitCode !== 0 || !/^[0-9a-f]{40}$/u.test(output))
|
|
41
|
+
throw new Error('Unable to resolve the git HEAD sha; pass --sha explicitly.')
|
|
42
|
+
return output
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function main(): Promise<void> {
|
|
46
|
+
const options = parseArguments(Bun.argv.slice(2))
|
|
47
|
+
const rootDir = join(import.meta.dir, '..')
|
|
48
|
+
const packageJson = JSON.parse(await readFile(join(rootDir, 'package.json'), 'utf8')) as { version: string }
|
|
49
|
+
const version = options.version ?? packageJson.version
|
|
50
|
+
const sha = options.sha ?? await resolveGitSha()
|
|
51
|
+
if (!/^[0-9a-f]{40}$/u.test(sha))
|
|
52
|
+
throw new Error(`Invalid source sha: ${sha}`)
|
|
53
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version))
|
|
54
|
+
throw new Error(`Invalid version: ${version}`)
|
|
55
|
+
|
|
56
|
+
const content = [
|
|
57
|
+
'// Generated by scripts/write-provenance.ts. Do not edit by hand.',
|
|
58
|
+
'export const generatedProvenance = {',
|
|
59
|
+
` version: ${JSON.stringify(version)},`,
|
|
60
|
+
` sha: ${JSON.stringify(sha)},`,
|
|
61
|
+
` buildTime: ${JSON.stringify(new Date().toISOString())},`,
|
|
62
|
+
'} as const',
|
|
63
|
+
'',
|
|
64
|
+
].join('\n')
|
|
65
|
+
|
|
66
|
+
if (options.dryRun) {
|
|
67
|
+
console.log(content)
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
await writeFile(join(rootDir, 'src', 'provenance.gen.ts'), content)
|
|
71
|
+
console.log(`Wrote src/provenance.gen.ts for ${version}@${sha.slice(0, 12)}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await main()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type AppErrorCategory = 'application' | 'provider' | 'infrastructure' | 'validation'
|
|
2
|
+
|
|
3
|
+
export interface AppErrorInput {
|
|
4
|
+
category: AppErrorCategory
|
|
5
|
+
code: string
|
|
6
|
+
message: string
|
|
7
|
+
publicMessage?: string
|
|
8
|
+
details?: Record<string, unknown>
|
|
9
|
+
retryable?: boolean
|
|
10
|
+
cause?: unknown
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class AppError extends Error {
|
|
14
|
+
readonly category: AppErrorCategory
|
|
15
|
+
readonly code: string
|
|
16
|
+
readonly publicMessage: string
|
|
17
|
+
readonly details?: Record<string, unknown>
|
|
18
|
+
readonly retryable: boolean
|
|
19
|
+
override readonly cause?: unknown
|
|
20
|
+
|
|
21
|
+
constructor(input: AppErrorInput) {
|
|
22
|
+
if (input.cause === undefined)
|
|
23
|
+
super(input.message)
|
|
24
|
+
else
|
|
25
|
+
super(input.message, { cause: input.cause })
|
|
26
|
+
|
|
27
|
+
this.name = new.target.name
|
|
28
|
+
this.category = input.category
|
|
29
|
+
this.code = input.code
|
|
30
|
+
this.publicMessage = input.publicMessage ?? input.message
|
|
31
|
+
this.retryable = input.retryable ?? false
|
|
32
|
+
|
|
33
|
+
if (input.details) {
|
|
34
|
+
this.details = input.details
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (input.cause !== undefined) {
|
|
38
|
+
this.cause = input.cause
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Error.captureStackTrace?.(this, new.target)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
toJSON() {
|
|
45
|
+
return {
|
|
46
|
+
name: this.name,
|
|
47
|
+
category: this.category,
|
|
48
|
+
code: this.code,
|
|
49
|
+
message: this.message,
|
|
50
|
+
publicMessage: this.publicMessage,
|
|
51
|
+
details: this.details,
|
|
52
|
+
retryable: this.retryable,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getPublicResponseBody() {
|
|
57
|
+
return {
|
|
58
|
+
message: this.publicMessage,
|
|
59
|
+
details: this.details,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isAppError(error: unknown): error is AppError {
|
|
65
|
+
return error instanceof AppError
|
|
66
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AppErrorInput } from './app-error'
|
|
2
|
+
import { AppError } from './app-error'
|
|
3
|
+
|
|
4
|
+
export type ApplicationErrorInput = Omit<AppErrorInput, 'category'>
|
|
5
|
+
|
|
6
|
+
export class ApplicationError extends AppError {
|
|
7
|
+
constructor(input: ApplicationErrorInput) {
|
|
8
|
+
super({
|
|
9
|
+
...input,
|
|
10
|
+
category: 'application',
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class RateLimitExceededError extends ApplicationError {
|
|
16
|
+
readonly retryAfterSeconds: number
|
|
17
|
+
|
|
18
|
+
constructor({ retryAfterSeconds, ...input }: Omit<ApplicationErrorInput, 'retryable' | 'details'> & {
|
|
19
|
+
retryAfterSeconds: number
|
|
20
|
+
}) {
|
|
21
|
+
if (!Number.isSafeInteger(retryAfterSeconds) || retryAfterSeconds <= 0)
|
|
22
|
+
throw new RangeError('retryAfterSeconds must be a positive safe integer')
|
|
23
|
+
|
|
24
|
+
super({
|
|
25
|
+
...input,
|
|
26
|
+
retryable: false,
|
|
27
|
+
details: { retry_after_seconds: retryAfterSeconds },
|
|
28
|
+
})
|
|
29
|
+
this.retryAfterSeconds = retryAfterSeconds
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class NotFoundError extends ApplicationError {
|
|
34
|
+
constructor(input: ApplicationErrorInput) {
|
|
35
|
+
super(input)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class ConflictError extends ApplicationError {
|
|
40
|
+
constructor(input: ApplicationErrorInput) {
|
|
41
|
+
super(input)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class ForbiddenError extends ApplicationError {
|
|
46
|
+
constructor(input: ApplicationErrorInput) {
|
|
47
|
+
super(input)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class UnauthorizedError extends ApplicationError {
|
|
52
|
+
constructor(input: ApplicationErrorInput) {
|
|
53
|
+
super(input)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { AppError, isAppError } from './app-error'
|
|
2
|
+
export type { AppErrorCategory, AppErrorInput } from './app-error'
|
|
3
|
+
export {
|
|
4
|
+
ApplicationError,
|
|
5
|
+
ConflictError,
|
|
6
|
+
ForbiddenError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
UnauthorizedError,
|
|
9
|
+
} from './application-error'
|
|
10
|
+
export type { ApplicationErrorInput } from './application-error'
|
|
11
|
+
export { InfrastructureError } from './infrastructure-error'
|
|
12
|
+
export type { InfrastructureErrorInput } from './infrastructure-error'
|
|
13
|
+
export { ProviderError } from './provider-error'
|
|
14
|
+
export type { ProviderErrorInput } from './provider-error'
|
|
15
|
+
export { UnprocessableEntityError, ValidationError } from './validation-error'
|
|
16
|
+
export type { ValidationErrorInput } from './validation-error'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppErrorInput } from './app-error'
|
|
2
|
+
import { AppError } from './app-error'
|
|
3
|
+
|
|
4
|
+
export type InfrastructureErrorInput = Omit<AppErrorInput, 'category'>
|
|
5
|
+
|
|
6
|
+
export class InfrastructureError extends AppError {
|
|
7
|
+
constructor(input: InfrastructureErrorInput) {
|
|
8
|
+
super({
|
|
9
|
+
...input,
|
|
10
|
+
category: 'infrastructure',
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AppErrorInput } from './app-error'
|
|
2
|
+
import { AppError } from './app-error'
|
|
3
|
+
|
|
4
|
+
export type ProviderErrorInput = Omit<AppErrorInput, 'category'>
|
|
5
|
+
|
|
6
|
+
export class ProviderError extends AppError {
|
|
7
|
+
constructor(input: ProviderErrorInput) {
|
|
8
|
+
super({
|
|
9
|
+
...input,
|
|
10
|
+
category: 'provider',
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AppErrorInput } from './app-error'
|
|
2
|
+
import { AppError } from './app-error'
|
|
3
|
+
|
|
4
|
+
export type ValidationErrorInput = Omit<AppErrorInput, 'category'>
|
|
5
|
+
|
|
6
|
+
export class ValidationError extends AppError {
|
|
7
|
+
constructor(input: ValidationErrorInput) {
|
|
8
|
+
super({
|
|
9
|
+
...input,
|
|
10
|
+
category: 'validation',
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class UnprocessableEntityError extends ValidationError {
|
|
16
|
+
constructor(input: ValidationErrorInput) {
|
|
17
|
+
super(input)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { LoggerOptions, Logger as PinoLogger } from 'pino'
|
|
2
|
+
import pino from 'pino'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Structural logger contract used across agent-core. Pino satisfies it, and so
|
|
6
|
+
* does any product logger (Datadog-flavoured pino wrappers included), so hosts
|
|
7
|
+
* inject their own instance and agent-core never owns process-wide logging.
|
|
8
|
+
*/
|
|
9
|
+
export type Logger = PinoLogger<never>
|
|
10
|
+
|
|
11
|
+
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'
|
|
12
|
+
|
|
13
|
+
interface LoggerBaseFields {
|
|
14
|
+
readonly [key: string]: string | number | boolean | null | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CreateLoggerInput {
|
|
18
|
+
level?: LogLevel
|
|
19
|
+
base?: LoggerBaseFields
|
|
20
|
+
/** Redirect output (tests, supervisor stderr). Defaults to stdout. */
|
|
21
|
+
destination?: pino.DestinationStream
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type SafeErrorField = 'category' | 'code' | 'publicMessage' | 'retryable' | 'details'
|
|
25
|
+
|
|
26
|
+
function readErrorField(error: Error, field: SafeErrorField): unknown {
|
|
27
|
+
return Reflect.get(error, field) as unknown
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Keeps only safe outer fields and never traverses error causes, so a wrapped
|
|
31
|
+
// provider error cannot leak credentials through its cause chain.
|
|
32
|
+
export function serializeError(error: Error) {
|
|
33
|
+
const category = readErrorField(error, 'category')
|
|
34
|
+
const code = readErrorField(error, 'code')
|
|
35
|
+
const publicMessage = readErrorField(error, 'publicMessage')
|
|
36
|
+
const retryable = readErrorField(error, 'retryable')
|
|
37
|
+
const details = readErrorField(error, 'details')
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
type: error.name || error.constructor.name,
|
|
41
|
+
message: error.message,
|
|
42
|
+
...(error.stack ? { stack: error.stack } : {}),
|
|
43
|
+
...(typeof category === 'string' ? { category } : {}),
|
|
44
|
+
...(typeof code === 'string' ? { code } : {}),
|
|
45
|
+
...(typeof publicMessage === 'string' ? { publicMessage } : {}),
|
|
46
|
+
...(typeof retryable === 'boolean' ? { retryable } : {}),
|
|
47
|
+
...(details ? { details } : {}),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readLogLevelFromEnvironment(environment: Record<string, string | undefined> = process.env): LogLevel {
|
|
52
|
+
const level = environment.LOG_LEVEL
|
|
53
|
+
if (level === 'trace' || level === 'debug' || level === 'info' || level === 'warn' || level === 'error' || level === 'fatal')
|
|
54
|
+
return level
|
|
55
|
+
return 'info'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createLogger(input: CreateLoggerInput = {}): Logger {
|
|
59
|
+
const options: LoggerOptions<never> = {
|
|
60
|
+
level: input.level ?? readLogLevelFromEnvironment(),
|
|
61
|
+
errorKey: 'err',
|
|
62
|
+
messageKey: 'message',
|
|
63
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
64
|
+
serializers: { err: serializeError },
|
|
65
|
+
formatters: {
|
|
66
|
+
level: (label: string) => ({ level: label.toUpperCase() }),
|
|
67
|
+
},
|
|
68
|
+
...(input.base ? { base: input.base } : {}),
|
|
69
|
+
}
|
|
70
|
+
return input.destination ? pino(options, input.destination) : pino(options)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function createSilentLogger(): Logger {
|
|
74
|
+
return pino({ level: 'silent' })
|
|
75
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { agentPromptSchema } from './agent-content'
|
|
3
|
+
import { agentProviderIdSchema } from './agent-provider'
|
|
4
|
+
import { agentUserInputAnswersSchema } from './agent-user-input'
|
|
5
|
+
|
|
6
|
+
export const agentReasoningEffortSchema = z.enum(['low', 'medium', 'high', 'xhigh', 'max'])
|
|
7
|
+
export const agentPromptModeSchema = z.enum(['next', 'steer'])
|
|
8
|
+
|
|
9
|
+
export const agentAuthorSchema = z.object({
|
|
10
|
+
name: z.string().min(1),
|
|
11
|
+
email: z.string().min(1).optional(),
|
|
12
|
+
gitEmail: z.string().min(1).optional(),
|
|
13
|
+
}).strict()
|
|
14
|
+
|
|
15
|
+
const commandIdSchema = z.string().min(1)
|
|
16
|
+
|
|
17
|
+
const runningCommandBaseSchema = z.object({
|
|
18
|
+
provider: agentProviderIdSchema,
|
|
19
|
+
sessionId: z.string().min(1),
|
|
20
|
+
providerSessionId: z.string().min(1),
|
|
21
|
+
}).strict()
|
|
22
|
+
|
|
23
|
+
export const sendPromptCommandSchema = runningCommandBaseSchema.extend({
|
|
24
|
+
type: z.literal('agent.send-prompt'),
|
|
25
|
+
mode: agentPromptModeSchema,
|
|
26
|
+
origin: z.enum(['user', 'supervisor']).default('user'),
|
|
27
|
+
prompt: agentPromptSchema,
|
|
28
|
+
commandId: commandIdSchema,
|
|
29
|
+
author: agentAuthorSchema.optional(),
|
|
30
|
+
}).strict()
|
|
31
|
+
|
|
32
|
+
export const interruptAgentCommandSchema = runningCommandBaseSchema.extend({
|
|
33
|
+
type: z.literal('agent.interrupt'),
|
|
34
|
+
commandId: commandIdSchema,
|
|
35
|
+
turnId: z.string().min(1).optional(),
|
|
36
|
+
reason: z.string().min(1).optional(),
|
|
37
|
+
}).strict()
|
|
38
|
+
|
|
39
|
+
export const stopAgentCommandSchema = runningCommandBaseSchema.extend({
|
|
40
|
+
type: z.literal('agent.stop'),
|
|
41
|
+
reason: z.string().min(1).optional(),
|
|
42
|
+
}).strict()
|
|
43
|
+
|
|
44
|
+
export const respondUserInputAgentCommandSchema = runningCommandBaseSchema.extend({
|
|
45
|
+
type: z.literal('agent.respond-user-input'),
|
|
46
|
+
commandId: commandIdSchema,
|
|
47
|
+
requestId: z.string().min(1),
|
|
48
|
+
answers: agentUserInputAnswersSchema,
|
|
49
|
+
}).strict()
|
|
50
|
+
|
|
51
|
+
export const agentCommandSchema = z.union([
|
|
52
|
+
sendPromptCommandSchema,
|
|
53
|
+
interruptAgentCommandSchema,
|
|
54
|
+
stopAgentCommandSchema,
|
|
55
|
+
respondUserInputAgentCommandSchema,
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
export type AgentReasoningEffort = z.infer<typeof agentReasoningEffortSchema>
|
|
59
|
+
export type AgentPromptMode = z.infer<typeof agentPromptModeSchema>
|
|
60
|
+
export type AgentAuthor = z.infer<typeof agentAuthorSchema>
|
|
61
|
+
export type SendPromptCommand = z.infer<typeof sendPromptCommandSchema>
|
|
62
|
+
export type InterruptAgentCommand = z.infer<typeof interruptAgentCommandSchema>
|
|
63
|
+
export type StopAgentCommand = z.infer<typeof stopAgentCommandSchema>
|
|
64
|
+
export type RespondUserInputAgentCommand = z.infer<typeof respondUserInputAgentCommandSchema>
|
|
65
|
+
export type AgentCommand = z.infer<typeof agentCommandSchema>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
|
|
3
|
+
export const textAgentInputBlockSchema = z.object({
|
|
4
|
+
type: z.literal('text'),
|
|
5
|
+
text: z.string(),
|
|
6
|
+
}).strict()
|
|
7
|
+
|
|
8
|
+
export const urlAgentImageSourceSchema = z.object({
|
|
9
|
+
kind: z.literal('url'),
|
|
10
|
+
url: z.url(),
|
|
11
|
+
}).strict()
|
|
12
|
+
|
|
13
|
+
export const base64AgentImageSourceSchema = z.object({
|
|
14
|
+
kind: z.literal('base64'),
|
|
15
|
+
data: z.string().min(1),
|
|
16
|
+
mimeType: z.string().min(1),
|
|
17
|
+
}).strict()
|
|
18
|
+
|
|
19
|
+
export const agentImageSourceSchema = z.discriminatedUnion('kind', [
|
|
20
|
+
urlAgentImageSourceSchema,
|
|
21
|
+
base64AgentImageSourceSchema,
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
export const imageAgentInputBlockSchema = z.object({
|
|
25
|
+
type: z.literal('image'),
|
|
26
|
+
source: agentImageSourceSchema,
|
|
27
|
+
}).strict()
|
|
28
|
+
|
|
29
|
+
export const textAgentFileSourceSchema = z.object({
|
|
30
|
+
kind: z.literal('text'),
|
|
31
|
+
name: z.string().min(1).optional(),
|
|
32
|
+
text: z.string(),
|
|
33
|
+
mimeType: z.string().min(1).optional(),
|
|
34
|
+
}).strict()
|
|
35
|
+
|
|
36
|
+
export const urlAgentFileSourceSchema = z.object({
|
|
37
|
+
kind: z.literal('url'),
|
|
38
|
+
url: z.url(),
|
|
39
|
+
name: z.string().min(1).optional(),
|
|
40
|
+
mimeType: z.string().min(1).optional(),
|
|
41
|
+
}).strict()
|
|
42
|
+
|
|
43
|
+
export const agentFileSourceSchema = z.discriminatedUnion('kind', [
|
|
44
|
+
textAgentFileSourceSchema,
|
|
45
|
+
urlAgentFileSourceSchema,
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
export const fileAgentInputBlockSchema = z.object({
|
|
49
|
+
type: z.literal('file'),
|
|
50
|
+
source: agentFileSourceSchema,
|
|
51
|
+
}).strict()
|
|
52
|
+
|
|
53
|
+
export const agentInputBlockSchema = z.discriminatedUnion('type', [
|
|
54
|
+
textAgentInputBlockSchema,
|
|
55
|
+
imageAgentInputBlockSchema,
|
|
56
|
+
fileAgentInputBlockSchema,
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
export const agentPromptSchema = z.object({
|
|
60
|
+
blocks: z.array(agentInputBlockSchema).min(1),
|
|
61
|
+
}).strict()
|
|
62
|
+
|
|
63
|
+
export type TextAgentInputBlock = z.infer<typeof textAgentInputBlockSchema>
|
|
64
|
+
export type UrlAgentImageSource = z.infer<typeof urlAgentImageSourceSchema>
|
|
65
|
+
export type Base64AgentImageSource = z.infer<typeof base64AgentImageSourceSchema>
|
|
66
|
+
export type AgentImageSource = z.infer<typeof agentImageSourceSchema>
|
|
67
|
+
export type ImageAgentInputBlock = z.infer<typeof imageAgentInputBlockSchema>
|
|
68
|
+
export type TextAgentFileSource = z.infer<typeof textAgentFileSourceSchema>
|
|
69
|
+
export type UrlAgentFileSource = z.infer<typeof urlAgentFileSourceSchema>
|
|
70
|
+
export type AgentFileSource = z.infer<typeof agentFileSourceSchema>
|
|
71
|
+
export type FileAgentInputBlock = z.infer<typeof fileAgentInputBlockSchema>
|
|
72
|
+
export type AgentInputBlock = z.infer<typeof agentInputBlockSchema>
|
|
73
|
+
export type AgentPrompt = z.infer<typeof agentPromptSchema>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ApplicationErrorInput, ValidationErrorInput } from '../errors'
|
|
2
|
+
import { ApplicationError, ValidationError } from '../errors'
|
|
3
|
+
|
|
4
|
+
type AgentProtocolErrorInput = Omit<ApplicationErrorInput, 'code'> & {
|
|
5
|
+
code?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type AgentProtocolValidationErrorInput = Omit<ValidationErrorInput, 'code'> & {
|
|
9
|
+
code?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type AgentProtocolDetails = ValidationErrorInput['details']
|
|
13
|
+
|
|
14
|
+
interface InvalidAgentCommandErrorInput {
|
|
15
|
+
message?: string
|
|
16
|
+
details?: AgentProtocolDetails
|
|
17
|
+
cause?: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface InvalidAgentEventErrorInput {
|
|
21
|
+
message?: string
|
|
22
|
+
details?: AgentProtocolDetails
|
|
23
|
+
cause?: unknown
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class AgentProtocolError extends ApplicationError {
|
|
27
|
+
constructor(input: AgentProtocolErrorInput) {
|
|
28
|
+
const { code = 'agents-protocol.error', ...rest } = input
|
|
29
|
+
|
|
30
|
+
super({
|
|
31
|
+
...rest,
|
|
32
|
+
code,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class AgentProtocolValidationError extends ValidationError {
|
|
38
|
+
constructor(input: AgentProtocolValidationErrorInput) {
|
|
39
|
+
const { code = 'agents-protocol.validation-failed', ...rest } = input
|
|
40
|
+
|
|
41
|
+
super({
|
|
42
|
+
...rest,
|
|
43
|
+
code,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class UnsupportedAgentProviderError extends AgentProtocolValidationError {
|
|
49
|
+
constructor({ provider }: { provider: string }) {
|
|
50
|
+
super({
|
|
51
|
+
code: 'agents-protocol.unsupported-provider',
|
|
52
|
+
message: `Unsupported agent provider: ${provider}`,
|
|
53
|
+
publicMessage: 'Unsupported agent provider.',
|
|
54
|
+
details: { provider },
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class InvalidAgentCommandError extends AgentProtocolValidationError {
|
|
60
|
+
constructor(input: InvalidAgentCommandErrorInput = {}) {
|
|
61
|
+
super({
|
|
62
|
+
code: 'agents-protocol.invalid-command',
|
|
63
|
+
message: input.message ?? 'Agent command is invalid.',
|
|
64
|
+
publicMessage: 'Agent command is invalid.',
|
|
65
|
+
details: input.details,
|
|
66
|
+
cause: input.cause,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class InvalidAgentEventError extends AgentProtocolValidationError {
|
|
72
|
+
constructor(input: InvalidAgentEventErrorInput = {}) {
|
|
73
|
+
super({
|
|
74
|
+
code: 'agents-protocol.invalid-event',
|
|
75
|
+
message: input.message ?? 'Agent event is invalid.',
|
|
76
|
+
publicMessage: 'Agent event is invalid.',
|
|
77
|
+
details: input.details,
|
|
78
|
+
cause: input.cause,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|