@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
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { agentPromptModeSchema, agentPromptSchema, agentUserInputAnswersSchema, jsonObjectSchema, jsonUtf8ByteLength } from '../protocol'
|
|
3
|
+
|
|
4
|
+
const wireSenderContextSchema = z.discriminatedUnion('kind', [
|
|
5
|
+
z.object({ kind: z.literal('application') }).strict(),
|
|
6
|
+
z.object({
|
|
7
|
+
kind: z.literal('slack'),
|
|
8
|
+
mention: z.string().max(256).refine(value => value.trim().length > 0),
|
|
9
|
+
}).strict(),
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
const wireCommandUserSchema = z.object({
|
|
13
|
+
id: z.string().min(1),
|
|
14
|
+
name: z.string().min(1),
|
|
15
|
+
email: z.string().min(1).optional(),
|
|
16
|
+
gitEmail: z.string().min(1).optional(),
|
|
17
|
+
senderContext: wireSenderContextSchema.optional(),
|
|
18
|
+
}).strict()
|
|
19
|
+
|
|
20
|
+
const wireInputAttachmentRefSchema = z.object({
|
|
21
|
+
attachmentId: z.string().refine(value => value.trim().length > 0),
|
|
22
|
+
ordinal: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
23
|
+
filename: z.string().refine(value => value.trim().length > 0),
|
|
24
|
+
mediaType: z.string().refine(value => value.trim().length > 0),
|
|
25
|
+
byteSize: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
26
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
27
|
+
}).strict()
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Product-specific prompt context (agent-api: declared inputs, run id, model
|
|
31
|
+
* overrides). Opaque to agent-core; interpreted by the host's
|
|
32
|
+
* `WorkspacePreparer.prepareTurn`.
|
|
33
|
+
*/
|
|
34
|
+
export const wireCommandExtensionsMaxBytes = 32 * 1024
|
|
35
|
+
export const wireCommandExtensionsSchema = jsonObjectSchema.refine(
|
|
36
|
+
value => jsonUtf8ByteLength(value) <= wireCommandExtensionsMaxBytes,
|
|
37
|
+
{ message: `command extensions must encode to at most ${wireCommandExtensionsMaxBytes} bytes` },
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const wireSendPromptCommandSchema = z.object({
|
|
41
|
+
type: z.literal('agent.send-prompt'),
|
|
42
|
+
mode: agentPromptModeSchema,
|
|
43
|
+
prompt: agentPromptSchema,
|
|
44
|
+
attachments: z.array(wireInputAttachmentRefSchema).superRefine((attachments, context) => {
|
|
45
|
+
const attachmentIds = new Set<string>()
|
|
46
|
+
for (const [index, attachment] of attachments.entries()) {
|
|
47
|
+
if (attachmentIds.has(attachment.attachmentId)) {
|
|
48
|
+
context.addIssue({ code: 'custom', path: [index, 'attachmentId'], message: 'attachmentId must be unique' })
|
|
49
|
+
}
|
|
50
|
+
attachmentIds.add(attachment.attachmentId)
|
|
51
|
+
if (attachment.ordinal !== index) {
|
|
52
|
+
context.addIssue({ code: 'custom', path: [index, 'ordinal'], message: 'ordinals must be contiguous and ordered' })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
user: wireCommandUserSchema.optional(),
|
|
57
|
+
extensions: wireCommandExtensionsSchema.optional(),
|
|
58
|
+
}).strict()
|
|
59
|
+
|
|
60
|
+
const wireInterruptCommandSchema = z.object({
|
|
61
|
+
type: z.literal('agent.interrupt'),
|
|
62
|
+
}).strict()
|
|
63
|
+
|
|
64
|
+
const wireRespondUserInputCommandSchema = z.object({
|
|
65
|
+
type: z.literal('agent.respond-user-input'),
|
|
66
|
+
requestId: z.string().min(1),
|
|
67
|
+
answers: agentUserInputAnswersSchema,
|
|
68
|
+
user: wireCommandUserSchema.optional(),
|
|
69
|
+
}).strict()
|
|
70
|
+
|
|
71
|
+
export const wireCommandBodySchema = z.discriminatedUnion('type', [
|
|
72
|
+
wireSendPromptCommandSchema,
|
|
73
|
+
wireInterruptCommandSchema,
|
|
74
|
+
wireRespondUserInputCommandSchema,
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
export type WireCommandUser = z.infer<typeof wireCommandUserSchema>
|
|
78
|
+
export type WireInputAttachmentRef = z.infer<typeof wireInputAttachmentRefSchema>
|
|
79
|
+
export type WireCommandExtensions = z.infer<typeof wireCommandExtensionsSchema>
|
|
80
|
+
export type WireSendPromptCommand = z.infer<typeof wireSendPromptCommandSchema>
|
|
81
|
+
export type WireRespondUserInputCommand = z.infer<typeof wireRespondUserInputCommandSchema>
|
|
82
|
+
export type WireCommandBody = z.infer<typeof wireCommandBodySchema>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer'
|
|
2
|
+
|
|
3
|
+
import z from 'zod'
|
|
4
|
+
|
|
5
|
+
export const contextFileContentTypeHeader = 'content-type' as const
|
|
6
|
+
export const contextFileCacheControlHeader = 'cache-control' as const
|
|
7
|
+
export const contextFileCacheControlValue = 'private, no-store' as const
|
|
8
|
+
export const contextFileNameHeader = 'x-coding-agent-context-file-name' as const
|
|
9
|
+
export const contextFileDeclaredSizeHeader = 'x-coding-agent-context-file-declared-size' as const
|
|
10
|
+
export const contextFileMaxBytes = 104_857_600
|
|
11
|
+
export const contextFileErrorBodyMaxBytes = 16 * 1024
|
|
12
|
+
export const contextFileDownloadTimeoutMs = 600_000
|
|
13
|
+
|
|
14
|
+
const contextFileMaxReferenceCharacters = 255
|
|
15
|
+
const contextFileMaxFilenameBytes = 255
|
|
16
|
+
const contextFileMaxEncodedFilenameCharacters = 340
|
|
17
|
+
const contextFileMediaTypePattern = /^[\w!#$&^.+-]+\/[\w!#$&^.+-]+$/u
|
|
18
|
+
const contextFileEncodedFilenamePattern = /^[\w-]{2,340}$/u
|
|
19
|
+
|
|
20
|
+
export const contextFileRefSchema = z.string()
|
|
21
|
+
.min(1)
|
|
22
|
+
.max(contextFileMaxReferenceCharacters)
|
|
23
|
+
.refine(value => value.trim().length > 0)
|
|
24
|
+
.refine(value => value !== '.' && value !== '..')
|
|
25
|
+
|
|
26
|
+
export const contextFileFilenameSchema = z.string()
|
|
27
|
+
.min(1)
|
|
28
|
+
.refine(value => Buffer.byteLength(value, 'utf8') <= contextFileMaxFilenameBytes)
|
|
29
|
+
|
|
30
|
+
export const contextFileMediaTypeSchema = z.string().regex(contextFileMediaTypePattern)
|
|
31
|
+
|
|
32
|
+
export const contextFileDeclaredByteSizeSchema = z.number()
|
|
33
|
+
.int()
|
|
34
|
+
.nonnegative()
|
|
35
|
+
.max(contextFileMaxBytes)
|
|
36
|
+
|
|
37
|
+
export const contextFileErrorResponseSchema = z.object({
|
|
38
|
+
message: z.string().min(1).max(500),
|
|
39
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
40
|
+
}).strict()
|
|
41
|
+
|
|
42
|
+
export type ContextFileErrorResponse = z.infer<typeof contextFileErrorResponseSchema>
|
|
43
|
+
|
|
44
|
+
export function encodeContextFileName(filename: string): string {
|
|
45
|
+
const parsed = contextFileFilenameSchema.parse(filename)
|
|
46
|
+
return Buffer.from(parsed, 'utf8').toString('base64url')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function decodeContextFileName(encoded: string): string | undefined {
|
|
50
|
+
if (encoded.length > contextFileMaxEncodedFilenameCharacters
|
|
51
|
+
|| !contextFileEncodedFilenamePattern.test(encoded)) {
|
|
52
|
+
return undefined
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const bytes = Buffer.from(encoded, 'base64url')
|
|
56
|
+
if (bytes.toString('base64url') !== encoded)
|
|
57
|
+
return undefined
|
|
58
|
+
|
|
59
|
+
let filename: string
|
|
60
|
+
try {
|
|
61
|
+
filename = new TextDecoder('utf-8', { fatal: true }).decode(bytes)
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parsed = contextFileFilenameSchema.safeParse(filename)
|
|
68
|
+
return parsed.success ? parsed.data : undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function normalizeContextFileMediaType(mediaType: string | undefined): string {
|
|
72
|
+
const parsed = contextFileMediaTypeSchema.safeParse(mediaType)
|
|
73
|
+
return parsed.success ? parsed.data : 'application/octet-stream'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function decodeContextFileErrorResponse(body: Uint8Array): ContextFileErrorResponse | undefined {
|
|
77
|
+
if (body.byteLength > contextFileErrorBodyMaxBytes)
|
|
78
|
+
return undefined
|
|
79
|
+
|
|
80
|
+
let value: unknown
|
|
81
|
+
try {
|
|
82
|
+
value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(body))
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const parsed = contextFileErrorResponseSchema.safeParse(value)
|
|
89
|
+
return parsed.success ? parsed.data : undefined
|
|
90
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const sandboxControlAuthorityAlgorithm = 'ES256' as const
|
|
4
|
+
export const sandboxControlAuthorityAudience = 'coding-agent:sandbox-supervisor-control' as const
|
|
5
|
+
export const sandboxControlAuthorityHeaderName = 'X-Coding-Agent-Control-Authority' as const
|
|
6
|
+
export const sandboxControlAuthorityAssertionLifetimeSeconds = 15
|
|
7
|
+
export const sandboxControlAuthorityClockSkewSeconds = 5
|
|
8
|
+
|
|
9
|
+
const nonemptyBoundedStringSchema = z.string().min(1).max(512)
|
|
10
|
+
const base64UrlCoordinateSchema = z.string().regex(/^[\w-]+$/u)
|
|
11
|
+
const keyIdSchema = z.string().regex(/^[\w.-]{1,128}$/u)
|
|
12
|
+
const positiveSafeIntegerSchema = z.number().int().positive().max(Number.MAX_SAFE_INTEGER)
|
|
13
|
+
const nonnegativeSafeIntegerSchema = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER)
|
|
14
|
+
const numericDateSchema = z.number().int().nonnegative()
|
|
15
|
+
|
|
16
|
+
export const sandboxControlAuthorityProtectedHeaderSchema = z.object({
|
|
17
|
+
alg: z.literal(sandboxControlAuthorityAlgorithm),
|
|
18
|
+
kid: keyIdSchema,
|
|
19
|
+
typ: z.literal('JWT'),
|
|
20
|
+
}).strict()
|
|
21
|
+
|
|
22
|
+
export const sandboxControlAuthorityPublicJwkSchema = z.object({
|
|
23
|
+
kty: z.literal('EC'),
|
|
24
|
+
crv: z.literal('P-256'),
|
|
25
|
+
x: base64UrlCoordinateSchema,
|
|
26
|
+
y: base64UrlCoordinateSchema,
|
|
27
|
+
kid: keyIdSchema,
|
|
28
|
+
alg: z.literal(sandboxControlAuthorityAlgorithm),
|
|
29
|
+
use: z.literal('sig'),
|
|
30
|
+
key_ops: z.tuple([z.literal('verify')]),
|
|
31
|
+
}).strict()
|
|
32
|
+
|
|
33
|
+
export const sandboxControlAuthorityPublicJwksSchema = z.object({
|
|
34
|
+
keys: z.array(sandboxControlAuthorityPublicJwkSchema).min(1),
|
|
35
|
+
}).strict().superRefine((jwks, context) => {
|
|
36
|
+
const keyIds = new Set<string>()
|
|
37
|
+
for (const [index, key] of jwks.keys.entries()) {
|
|
38
|
+
if (keyIds.has(key.kid)) {
|
|
39
|
+
context.addIssue({
|
|
40
|
+
code: 'custom',
|
|
41
|
+
path: ['keys', index, 'kid'],
|
|
42
|
+
message: 'Control-authority key IDs must be unique.',
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
keyIds.add(key.kid)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const sandboxControlAuthorityClaimsSchema = z.object({
|
|
50
|
+
iss: nonemptyBoundedStringSchema,
|
|
51
|
+
aud: z.literal(sandboxControlAuthorityAudience),
|
|
52
|
+
jti: z.uuid(),
|
|
53
|
+
iat: numericDateSchema,
|
|
54
|
+
nbf: numericDateSchema,
|
|
55
|
+
exp: numericDateSchema,
|
|
56
|
+
providerSandboxId: nonemptyBoundedStringSchema,
|
|
57
|
+
sessionId: nonemptyBoundedStringSchema,
|
|
58
|
+
sessionSandboxId: nonemptyBoundedStringSchema,
|
|
59
|
+
connectionGeneration: positiveSafeIntegerSchema,
|
|
60
|
+
runtimeConnectionAttemptId: z.uuid(),
|
|
61
|
+
eventAckFloor: nonnegativeSafeIntegerSchema,
|
|
62
|
+
}).strict().superRefine((claims, context) => {
|
|
63
|
+
if (claims.jti !== claims.runtimeConnectionAttemptId) {
|
|
64
|
+
context.addIssue({
|
|
65
|
+
code: 'custom',
|
|
66
|
+
path: ['jti'],
|
|
67
|
+
message: 'Assertion ID must equal the runtime connection attempt ID.',
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
if (claims.nbf < claims.iat) {
|
|
71
|
+
context.addIssue({
|
|
72
|
+
code: 'custom',
|
|
73
|
+
path: ['nbf'],
|
|
74
|
+
message: 'Not-before time must not precede issued-at time.',
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
if (claims.exp <= claims.nbf) {
|
|
78
|
+
context.addIssue({
|
|
79
|
+
code: 'custom',
|
|
80
|
+
path: ['exp'],
|
|
81
|
+
message: 'Expiration time must follow not-before time.',
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
export type SandboxControlAuthorityClaims = z.infer<typeof sandboxControlAuthorityClaimsSchema>
|
|
87
|
+
export type SandboxControlAuthorityProtectedHeader = z.infer<typeof sandboxControlAuthorityProtectedHeaderSchema>
|
|
88
|
+
export type SandboxControlAuthorityPublicJwks = z.infer<typeof sandboxControlAuthorityPublicJwksSchema>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { WrappedAgentEvent } from './agent-event-wrapper'
|
|
2
|
+
import type { WireEventBody, WireEventBodyType } from './event-body'
|
|
3
|
+
|
|
4
|
+
type WireEventDurability = 'durable' | 'ephemeral'
|
|
5
|
+
|
|
6
|
+
const ephemeralEventTypes = [
|
|
7
|
+
'agent.message.delta',
|
|
8
|
+
'agent.tool.output.delta',
|
|
9
|
+
'agent.reasoning.summary.delta',
|
|
10
|
+
'supervisor.heartbeat',
|
|
11
|
+
] as const satisfies readonly WireEventBodyType[]
|
|
12
|
+
|
|
13
|
+
type EphemeralWireEventType = typeof ephemeralEventTypes[number]
|
|
14
|
+
type EphemeralWrappedAgentEventType = Extract<EphemeralWireEventType, WrappedAgentEvent['type']>
|
|
15
|
+
|
|
16
|
+
export type EphemeralWrappedAgentEvent = Extract<
|
|
17
|
+
WrappedAgentEvent,
|
|
18
|
+
{ type: EphemeralWrappedAgentEventType }
|
|
19
|
+
>
|
|
20
|
+
|
|
21
|
+
export type DurableWrappedAgentEvent = Exclude<WrappedAgentEvent, EphemeralWrappedAgentEvent>
|
|
22
|
+
|
|
23
|
+
const ephemeralEventTypeSet = new Set<string>(ephemeralEventTypes)
|
|
24
|
+
|
|
25
|
+
// Every product.* event is durable by construction; only the listed agent
|
|
26
|
+
// deltas and the supervisor heartbeat are ephemeral.
|
|
27
|
+
export function durabilityOf(type: WireEventBodyType): WireEventDurability {
|
|
28
|
+
return ephemeralEventTypeSet.has(type) ? 'ephemeral' : 'durable'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isEphemeralWrappedAgentEvent(body: WireEventBody): body is EphemeralWrappedAgentEvent {
|
|
32
|
+
return body.type !== 'supervisor.heartbeat' && ephemeralEventTypeSet.has(body.type)
|
|
33
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { controlPlaneToSupervisorEnvelopeSchema } from './envelopes/control-plane-to-supervisor'
|
|
3
|
+
import { supervisorToControlPlaneEnvelopeSchema } from './envelopes/supervisor-to-control-plane'
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
commandSequenceSchema,
|
|
7
|
+
connectionGenerationSchema,
|
|
8
|
+
eventSequenceSchema,
|
|
9
|
+
sequenceFloorSchema,
|
|
10
|
+
timestampSchema,
|
|
11
|
+
} from './envelopes/common'
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
commandEnvelopeSchema,
|
|
15
|
+
controlPlaneToSupervisorEnvelopeKinds,
|
|
16
|
+
controlPlaneToSupervisorEnvelopeSchema,
|
|
17
|
+
eventAckEnvelopeSchema,
|
|
18
|
+
sessionBootstrapEnvelopeSchema,
|
|
19
|
+
} from './envelopes/control-plane-to-supervisor'
|
|
20
|
+
export type {
|
|
21
|
+
CommandEnvelope,
|
|
22
|
+
ControlPlaneToSupervisorEnvelope,
|
|
23
|
+
EventAckEnvelope,
|
|
24
|
+
SessionBootstrapEnvelope,
|
|
25
|
+
} from './envelopes/control-plane-to-supervisor'
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
commandAckEnvelopeSchema,
|
|
29
|
+
commandAckErrorCodeSchema,
|
|
30
|
+
commandAckStatusSchema,
|
|
31
|
+
durableEventEnvelopeSchema,
|
|
32
|
+
ephemeralEventEnvelopeSchema,
|
|
33
|
+
eventDeliverySemanticsSchema,
|
|
34
|
+
eventEnvelopeSchema,
|
|
35
|
+
receivedCommandAckEnvelopeSchema,
|
|
36
|
+
rejectedCommandAckEnvelopeSchema,
|
|
37
|
+
runtimeStateEnvelopeSchema,
|
|
38
|
+
supervisorToControlPlaneEnvelopeKinds,
|
|
39
|
+
supervisorToControlPlaneEnvelopeSchema,
|
|
40
|
+
} from './envelopes/supervisor-to-control-plane'
|
|
41
|
+
export type {
|
|
42
|
+
CommandAckEnvelope,
|
|
43
|
+
CommandAckStatus,
|
|
44
|
+
DurableEventEnvelope,
|
|
45
|
+
EphemeralEventEnvelope,
|
|
46
|
+
EventEnvelope,
|
|
47
|
+
ReceivedCommandAckEnvelope,
|
|
48
|
+
RejectedCommandAckEnvelope,
|
|
49
|
+
RuntimeStateEnvelope,
|
|
50
|
+
SupervisorToControlPlaneEnvelope,
|
|
51
|
+
} from './envelopes/supervisor-to-control-plane'
|
|
52
|
+
|
|
53
|
+
export const wireEnvelopeSchema = z.union([
|
|
54
|
+
controlPlaneToSupervisorEnvelopeSchema,
|
|
55
|
+
supervisorToControlPlaneEnvelopeSchema,
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
export type WireEnvelope = z.infer<typeof wireEnvelopeSchema>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
|
|
3
|
+
export const eventSequenceSchema = z.number().int().positive()
|
|
4
|
+
export const sequenceFloorSchema = z.number().int().nonnegative()
|
|
5
|
+
export const commandSequenceSchema = z.number().int().positive()
|
|
6
|
+
export const connectionGenerationSchema = z.number().int().positive().max(Number.MAX_SAFE_INTEGER)
|
|
7
|
+
export const timestampSchema = z.string().datetime()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { ephemeralCredentialsSchema, sessionBootstrapBodySchema, sessionBootstrapGitTokenSchema } from '../bootstrap'
|
|
3
|
+
import { supervisorRpcResponseEnvelopeSchema } from '../rpc'
|
|
4
|
+
import { commandSequenceSchema, sequenceFloorSchema } from './common'
|
|
5
|
+
|
|
6
|
+
export const sessionBootstrapEnvelopeSchema = z.object({
|
|
7
|
+
kind: z.literal('session.bootstrap'),
|
|
8
|
+
connectionGeneration: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
9
|
+
body: sessionBootstrapBodySchema,
|
|
10
|
+
gitToken: sessionBootstrapGitTokenSchema.optional(),
|
|
11
|
+
credentials: ephemeralCredentialsSchema.optional(),
|
|
12
|
+
}).strict()
|
|
13
|
+
|
|
14
|
+
export const eventAckEnvelopeSchema = z.object({
|
|
15
|
+
kind: z.literal('event.ack'),
|
|
16
|
+
highWaterMark: sequenceFloorSchema,
|
|
17
|
+
}).strict()
|
|
18
|
+
|
|
19
|
+
export const commandEnvelopeSchema = z.object({
|
|
20
|
+
kind: z.literal('command'),
|
|
21
|
+
commandId: z.string().min(1),
|
|
22
|
+
commandSeq: commandSequenceSchema,
|
|
23
|
+
body: z.unknown(),
|
|
24
|
+
}).strict()
|
|
25
|
+
|
|
26
|
+
export const controlPlaneToSupervisorEnvelopeSchema = z.union([
|
|
27
|
+
sessionBootstrapEnvelopeSchema,
|
|
28
|
+
eventAckEnvelopeSchema,
|
|
29
|
+
commandEnvelopeSchema,
|
|
30
|
+
supervisorRpcResponseEnvelopeSchema,
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
export const controlPlaneToSupervisorEnvelopeKinds = ['session.bootstrap', 'event.ack', 'command', 'rpc.response'] as const
|
|
34
|
+
|
|
35
|
+
export type SessionBootstrapEnvelope = z.infer<typeof sessionBootstrapEnvelopeSchema>
|
|
36
|
+
export type EventAckEnvelope = z.infer<typeof eventAckEnvelopeSchema>
|
|
37
|
+
export type CommandEnvelope = z.infer<typeof commandEnvelopeSchema>
|
|
38
|
+
export type ControlPlaneToSupervisorEnvelope = z.infer<typeof controlPlaneToSupervisorEnvelopeSchema>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { durabilityOf } from '../durability'
|
|
3
|
+
import { wireEventBodySchema } from '../event-body'
|
|
4
|
+
import { supervisorRpcCancelEnvelopeSchema, supervisorRpcRequestEnvelopeSchema } from '../rpc'
|
|
5
|
+
import { supervisorAgentRunSnapshotSchema } from '../supervisor-agent-run-snapshot'
|
|
6
|
+
import { commandSequenceSchema, eventSequenceSchema, timestampSchema } from './common'
|
|
7
|
+
|
|
8
|
+
export const eventDeliverySemanticsSchema = z.enum(['durable', 'ephemeral'])
|
|
9
|
+
export const commandAckStatusSchema = z.enum(['received', 'rejected'])
|
|
10
|
+
export const commandAckErrorCodeSchema = z.string()
|
|
11
|
+
.min(3)
|
|
12
|
+
.max(100)
|
|
13
|
+
.regex(/^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+$/u)
|
|
14
|
+
|
|
15
|
+
export const runtimeStateEnvelopeSchema = z.object({
|
|
16
|
+
kind: z.literal('runtime.state'),
|
|
17
|
+
lastReceivedCommandSeq: z.number().int().nonnegative(),
|
|
18
|
+
nextEventSeq: eventSequenceSchema,
|
|
19
|
+
agentRun: supervisorAgentRunSnapshotSchema,
|
|
20
|
+
}).strict()
|
|
21
|
+
|
|
22
|
+
export const durableEventEnvelopeSchema = z.object({
|
|
23
|
+
kind: z.literal('event'),
|
|
24
|
+
delivery_semantics: z.literal('durable'),
|
|
25
|
+
seq: eventSequenceSchema,
|
|
26
|
+
occurredAt: timestampSchema,
|
|
27
|
+
body: wireEventBodySchema,
|
|
28
|
+
}).strict()
|
|
29
|
+
|
|
30
|
+
export const ephemeralEventEnvelopeSchema = z.object({
|
|
31
|
+
kind: z.literal('event'),
|
|
32
|
+
delivery_semantics: z.literal('ephemeral'),
|
|
33
|
+
occurredAt: timestampSchema,
|
|
34
|
+
body: wireEventBodySchema,
|
|
35
|
+
}).strict()
|
|
36
|
+
|
|
37
|
+
export const eventEnvelopeSchema = z.discriminatedUnion('delivery_semantics', [
|
|
38
|
+
durableEventEnvelopeSchema,
|
|
39
|
+
ephemeralEventEnvelopeSchema,
|
|
40
|
+
]).superRefine((envelope, context) => {
|
|
41
|
+
const expected = durabilityOf(envelope.body.type)
|
|
42
|
+
if (expected !== envelope.delivery_semantics) {
|
|
43
|
+
context.addIssue({
|
|
44
|
+
code: 'custom',
|
|
45
|
+
message: `Event body type "${envelope.body.type}" is ${expected}, but the envelope is marked ${envelope.delivery_semantics}.`,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const commandAckIdentitySchema = z.object({
|
|
51
|
+
kind: z.literal('command.ack'),
|
|
52
|
+
commandId: z.string().min(1),
|
|
53
|
+
commandSeq: commandSequenceSchema,
|
|
54
|
+
}).strict()
|
|
55
|
+
|
|
56
|
+
export const receivedCommandAckEnvelopeSchema = commandAckIdentitySchema.extend({
|
|
57
|
+
status: z.literal('received'),
|
|
58
|
+
}).strict()
|
|
59
|
+
|
|
60
|
+
export const rejectedCommandAckEnvelopeSchema = commandAckIdentitySchema.extend({
|
|
61
|
+
status: z.literal('rejected'),
|
|
62
|
+
errorCode: commandAckErrorCodeSchema,
|
|
63
|
+
detail: z.string().min(1).max(240).optional(),
|
|
64
|
+
}).strict()
|
|
65
|
+
|
|
66
|
+
export const commandAckEnvelopeSchema = z.discriminatedUnion('status', [
|
|
67
|
+
receivedCommandAckEnvelopeSchema,
|
|
68
|
+
rejectedCommandAckEnvelopeSchema,
|
|
69
|
+
])
|
|
70
|
+
|
|
71
|
+
export const supervisorToControlPlaneEnvelopeSchema = z.union([
|
|
72
|
+
runtimeStateEnvelopeSchema,
|
|
73
|
+
eventEnvelopeSchema,
|
|
74
|
+
commandAckEnvelopeSchema,
|
|
75
|
+
supervisorRpcRequestEnvelopeSchema,
|
|
76
|
+
supervisorRpcCancelEnvelopeSchema,
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
export const supervisorToControlPlaneEnvelopeKinds = ['runtime.state', 'event', 'command.ack', 'rpc.request', 'rpc.cancel'] as const
|
|
80
|
+
|
|
81
|
+
export type CommandAckStatus = z.infer<typeof commandAckStatusSchema>
|
|
82
|
+
export type ReceivedCommandAckEnvelope = z.infer<typeof receivedCommandAckEnvelopeSchema>
|
|
83
|
+
export type RejectedCommandAckEnvelope = z.infer<typeof rejectedCommandAckEnvelopeSchema>
|
|
84
|
+
export type RuntimeStateEnvelope = z.infer<typeof runtimeStateEnvelopeSchema>
|
|
85
|
+
export type DurableEventEnvelope = z.infer<typeof durableEventEnvelopeSchema>
|
|
86
|
+
export type EphemeralEventEnvelope = z.infer<typeof ephemeralEventEnvelopeSchema>
|
|
87
|
+
export type EventEnvelope = z.infer<typeof eventEnvelopeSchema>
|
|
88
|
+
export type CommandAckEnvelope = z.infer<typeof commandAckEnvelopeSchema>
|
|
89
|
+
export type SupervisorToControlPlaneEnvelope = z.infer<typeof supervisorToControlPlaneEnvelopeSchema>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { wrappedAgentEventSchema } from './agent-event-wrapper'
|
|
3
|
+
import { productEventSchema } from './product-event'
|
|
4
|
+
import { supervisorEventSchema } from './supervisor-event'
|
|
5
|
+
|
|
6
|
+
export const wireEventBodySchema = z.union([
|
|
7
|
+
wrappedAgentEventSchema,
|
|
8
|
+
supervisorEventSchema,
|
|
9
|
+
productEventSchema,
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
export type WireEventBody = z.infer<typeof wireEventBodySchema>
|
|
13
|
+
export type WireEventBodyType = WireEventBody['type']
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { agentPayloadOverflowSchema } from '../protocol'
|
|
2
|
+
export type { AgentPayloadOverflow } from '../protocol'
|
|
3
|
+
|
|
4
|
+
/** Hard cap on a single WebSocket frame in either direction. */
|
|
5
|
+
export const maxWireFrameBytes = 128 * 1024
|
|
6
|
+
/**
|
|
7
|
+
* Largest durable event body a supervisor may emit inline. Bodies above this
|
|
8
|
+
* are truncated to a bounded head and spilled through the host's
|
|
9
|
+
* `LargePayloadStore`, with the full value referenced by `overflow`.
|
|
10
|
+
*/
|
|
11
|
+
export const maxDurableEventBytes = 96 * 1024
|
|
12
|
+
/** Cap on each ephemeral streaming delta (message, reasoning, tool output). */
|
|
13
|
+
export const maxEphemeralDeltaBytes = 16 * 1024
|
|
14
|
+
/** Cap on a `product.*` event payload. */
|
|
15
|
+
export const maxProductEventPayloadBytes = 64 * 1024
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { jsonUtf8ByteLength, jsonValueSchema } from '../protocol'
|
|
3
|
+
import { maxProductEventPayloadBytes } from './payload-overflow'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Durable events emitted by a product's supervisor extensions (for example
|
|
7
|
+
* agent-api's `product.turn.result`). agent-core relays them untouched; only
|
|
8
|
+
* the type namespace and payload size are enforced here.
|
|
9
|
+
*/
|
|
10
|
+
export const productEventTypePattern = /^product\.[a-z][a-z0-9.-]+$/u
|
|
11
|
+
|
|
12
|
+
export const productEventTypeSchema = z.string().max(128).regex(productEventTypePattern)
|
|
13
|
+
|
|
14
|
+
export const productEventSchema = z.object({
|
|
15
|
+
type: productEventTypeSchema as z.ZodType<ProductEventType>,
|
|
16
|
+
payload: jsonValueSchema.refine(
|
|
17
|
+
value => jsonUtf8ByteLength(value) <= maxProductEventPayloadBytes,
|
|
18
|
+
{ message: `product event payload must encode to at most ${maxProductEventPayloadBytes} bytes` },
|
|
19
|
+
),
|
|
20
|
+
}).strict()
|
|
21
|
+
|
|
22
|
+
export type ProductEventType = `product.${string}`
|
|
23
|
+
export type ProductEvent = z.infer<typeof productEventSchema>
|
|
24
|
+
|
|
25
|
+
export function isProductEventType(type: string): type is ProductEventType {
|
|
26
|
+
return productEventTypePattern.test(type)
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { jsonObjectSchema, jsonUtf8ByteLength } from '../protocol'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A profile names the product that operates a sandbox (for example `agent-api`
|
|
6
|
+
* or `coding-agent`) and carries the product-specific bootstrap data its
|
|
7
|
+
* supervisor extensions consume. agent-core never interprets `data`; it only
|
|
8
|
+
* bounds it and hands it to the host's `SupervisorExtensions`.
|
|
9
|
+
*/
|
|
10
|
+
export const sessionProfileNameSchema = z.string().max(64).regex(/^[a-z][a-z0-9-]*$/u)
|
|
11
|
+
export const sessionProfileMaxDataBytes = 32 * 1024
|
|
12
|
+
|
|
13
|
+
export const sessionProfileSchema = z.object({
|
|
14
|
+
name: sessionProfileNameSchema,
|
|
15
|
+
version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
16
|
+
data: jsonObjectSchema.refine(
|
|
17
|
+
value => jsonUtf8ByteLength(value) <= sessionProfileMaxDataBytes,
|
|
18
|
+
{ message: `profile data must encode to at most ${sessionProfileMaxDataBytes} bytes` },
|
|
19
|
+
),
|
|
20
|
+
}).strict()
|
|
21
|
+
|
|
22
|
+
export type SessionProfile = z.infer<typeof sessionProfileSchema>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reverse RPC methods every supervisor may call on its control plane. The first
|
|
5
|
+
* ten are the coding-agent set; `credentials.issue` and `payload.put-grant` are
|
|
6
|
+
* the product-neutral additions agent-core hosts implement. Products register
|
|
7
|
+
* further methods under the `x-<profile>.` namespace.
|
|
8
|
+
*/
|
|
9
|
+
export const supervisorRpcMethods = [
|
|
10
|
+
'git.issue-token',
|
|
11
|
+
'git.resolve-repository',
|
|
12
|
+
'codex.auth.start',
|
|
13
|
+
'codex.auth.refresh',
|
|
14
|
+
'git.generate-commit-message',
|
|
15
|
+
'tool.execute',
|
|
16
|
+
'artifact.initiate',
|
|
17
|
+
'artifact.complete',
|
|
18
|
+
'input-attachment.get-content-grant',
|
|
19
|
+
'context-file.get-content-grant',
|
|
20
|
+
'credentials.issue',
|
|
21
|
+
'payload.put-grant',
|
|
22
|
+
] as const
|
|
23
|
+
|
|
24
|
+
export const productRpcMethodPattern = /^x-[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]+)+$/u
|
|
25
|
+
|
|
26
|
+
export const builtinSupervisorRpcMethodSchema = z.enum(supervisorRpcMethods)
|
|
27
|
+
export const productRpcMethodSchema = z.string().max(128).regex(productRpcMethodPattern)
|
|
28
|
+
const supervisorRpcMethodSchema = z.union([builtinSupervisorRpcMethodSchema, productRpcMethodSchema])
|
|
29
|
+
|
|
30
|
+
export type BuiltinSupervisorRpcMethod = typeof supervisorRpcMethods[number]
|
|
31
|
+
export type ProductRpcMethod = `x-${string}.${string}`
|
|
32
|
+
export type SupervisorRpcMethod = BuiltinSupervisorRpcMethod | ProductRpcMethod
|
|
33
|
+
|
|
34
|
+
export function isProductRpcMethod(method: string): method is ProductRpcMethod {
|
|
35
|
+
return productRpcMethodPattern.test(method)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isBuiltinSupervisorRpcMethod(method: string): method is BuiltinSupervisorRpcMethod {
|
|
39
|
+
return (supervisorRpcMethods as readonly string[]).includes(method)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const supervisorRpcRequestEnvelopeSchema = z.object({
|
|
43
|
+
kind: z.literal('rpc.request'),
|
|
44
|
+
requestId: z.string().min(1),
|
|
45
|
+
method: supervisorRpcMethodSchema as z.ZodType<SupervisorRpcMethod>,
|
|
46
|
+
deadlineMs: z.number().int().positive().max(10 * 60 * 1_000),
|
|
47
|
+
body: z.unknown(),
|
|
48
|
+
}).strict()
|
|
49
|
+
|
|
50
|
+
const supervisorRpcSuccessSchema = z.object({
|
|
51
|
+
status: z.literal('ok'),
|
|
52
|
+
value: z.unknown(),
|
|
53
|
+
}).strict()
|
|
54
|
+
|
|
55
|
+
const supervisorRpcFailureSchema = z.object({
|
|
56
|
+
status: z.literal('error'),
|
|
57
|
+
error: z.object({
|
|
58
|
+
code: z.string().min(1),
|
|
59
|
+
message: z.string().min(1),
|
|
60
|
+
retryable: z.boolean(),
|
|
61
|
+
}).strict(),
|
|
62
|
+
}).strict()
|
|
63
|
+
|
|
64
|
+
export const supervisorRpcCancelEnvelopeSchema = z.object({
|
|
65
|
+
kind: z.literal('rpc.cancel'),
|
|
66
|
+
requestId: z.string().min(1),
|
|
67
|
+
}).strict()
|
|
68
|
+
|
|
69
|
+
export const supervisorRpcResponseEnvelopeSchema = z.object({
|
|
70
|
+
kind: z.literal('rpc.response'),
|
|
71
|
+
requestId: z.string().min(1),
|
|
72
|
+
result: z.discriminatedUnion('status', [supervisorRpcSuccessSchema, supervisorRpcFailureSchema]),
|
|
73
|
+
}).strict()
|
|
74
|
+
|
|
75
|
+
export type SupervisorRpcRequestEnvelope = z.infer<typeof supervisorRpcRequestEnvelopeSchema>
|
|
76
|
+
export type SupervisorRpcCancelEnvelope = z.infer<typeof supervisorRpcCancelEnvelopeSchema>
|
|
77
|
+
export type SupervisorRpcResponseEnvelope = z.infer<typeof supervisorRpcResponseEnvelopeSchema>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import z from 'zod'
|
|
2
|
+
import { agentSessionStateSchema } from '../protocol'
|
|
3
|
+
|
|
4
|
+
export const supervisorAgentRunSnapshotSchema = z.discriminatedUnion('status', [
|
|
5
|
+
z.object({ status: z.literal('not_attached') }).strict(),
|
|
6
|
+
z.object({
|
|
7
|
+
status: z.literal('attached'),
|
|
8
|
+
agentState: agentSessionStateSchema.nullable(),
|
|
9
|
+
}).strict(),
|
|
10
|
+
])
|
|
11
|
+
|
|
12
|
+
export type SupervisorAgentRunSnapshot = z.infer<typeof supervisorAgentRunSnapshotSchema>
|