feat: foundation — Docker Compose, OTEL, shared types (P0-002, P0-005, P0-006)

Docker Compose (P0-005):
- PG 17 + pgvector, Valkey 8, OTEL Collector, Jaeger
- OTEL Collector config with trace/metric/log pipelines
- .env.example with all connection strings

OTEL Foundation (P0-006):
- @opentelemetry/sdk-node with auto-instrumentations
- tracing.ts loaded before NestJS bootstrap
- OTLP HTTP exporter → collector → Jaeger
- Disabled fs/dns instrumentations (noisy)

Shared Types (P0-002):
- ChatMessageDto with class-validator (IsUUID, IsNotEmpty, MaxLength)
- ChatResponseDto for REST responses
- Socket.IO typed event maps (ServerToClientEvents, ClientToServerEvents)
- AgentSessionHandle opaque type
- All event payload interfaces shared across gateway/cli/discord

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 20:55:04 -05:00
parent f6f05cf23a
commit a52537985e
14 changed files with 2055 additions and 26 deletions

View File

@@ -18,5 +18,9 @@
"devDependencies": {
"typescript": "^5.8.0",
"vitest": "^2.0.0"
},
"dependencies": {
"class-transformer": "^0.5.1",
"class-validator": "^0.15.1"
}
}

View File

@@ -0,0 +1,4 @@
/** Opaque handle for agent sessions — callers should not access internals */
export interface AgentSessionHandle {
readonly id: string;
}

View File

@@ -0,0 +1,17 @@
import { IsString, IsNotEmpty, IsOptional, IsUUID, MaxLength } from 'class-validator';
export class ChatMessageDto {
@IsOptional()
@IsUUID(4)
conversationId?: string;
@IsString()
@IsNotEmpty()
@MaxLength(32_000)
content!: string;
}
export class ChatResponseDto {
conversationId!: string;
text!: string;
}

View File

@@ -0,0 +1,62 @@
export interface MessageAckPayload {
conversationId: string;
messageId: string;
}
export interface AgentStartPayload {
conversationId: string;
}
export interface AgentEndPayload {
conversationId: string;
}
export interface AgentTextPayload {
conversationId: string;
text: string;
}
export interface AgentThinkingPayload {
conversationId: string;
text: string;
}
export interface ToolStartPayload {
conversationId: string;
toolCallId: string;
toolName: string;
}
export interface ToolEndPayload {
conversationId: string;
toolCallId: string;
toolName: string;
isError: boolean;
}
export interface ErrorPayload {
conversationId: string;
error: string;
}
export interface ChatMessagePayload {
conversationId?: string;
content: string;
}
/** Socket.IO typed event map: server → client */
export interface ServerToClientEvents {
'message:ack': (payload: MessageAckPayload) => void;
'agent:start': (payload: AgentStartPayload) => void;
'agent:end': (payload: AgentEndPayload) => void;
'agent:text': (payload: AgentTextPayload) => void;
'agent:thinking': (payload: AgentThinkingPayload) => void;
'agent:tool:start': (payload: ToolStartPayload) => void;
'agent:tool:end': (payload: ToolEndPayload) => void;
error: (payload: ErrorPayload) => void;
}
/** Socket.IO typed event map: client → server */
export interface ClientToServerEvents {
message: (data: ChatMessagePayload) => void;
}

View File

@@ -0,0 +1,14 @@
export { ChatMessageDto, ChatResponseDto } from './chat.dto.js';
export type {
MessageAckPayload,
AgentStartPayload,
AgentEndPayload,
AgentTextPayload,
AgentThinkingPayload,
ToolStartPayload,
ToolEndPayload,
ErrorPayload,
ChatMessagePayload,
ServerToClientEvents,
ClientToServerEvents,
} from './events.js';

View File

@@ -1 +1,4 @@
export const VERSION = '0.0.0';
export * from './chat/index.js';
export * from './agent/index.js';

View File

@@ -2,7 +2,9 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
"rootDir": "src",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]