diff --git a/.air.toml b/.air.toml index 8b8d40b3..44899831 100644 --- a/.air.toml +++ b/.air.toml @@ -7,7 +7,7 @@ bin = "./tmp/memoh-server" args_bin = ["serve"] include_ext = ["go", "toml"] include_dir = ["cmd", "internal", "conf"] -exclude_dir = ["tmp", "vendor", "node_modules", "packages", "agent", "docs", "devenv", "docker", "scripts", "db/migrations"] +exclude_dir = ["tmp", "vendor", "node_modules", "packages", "apps", "docs", "devenv", "docker", "scripts", "db/migrations"] exclude_regex = ["_test\\.go$"] delay = 1000 stop_on_error = true diff --git a/AGENTS.md b/AGENTS.md index 9687adf8..dbc144f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -100,13 +100,14 @@ Memoh/ │ ├── storage/ # Storage provider interface (filesystem, container FS) │ ├── subagent/ # Sub-agent management (CRUD) │ └── version/ # Build-time version information -├── agent/ # Agent Gateway service (Bun/Elysia) -│ └── src/ -│ ├── index.ts # Elysia server entry point -│ ├── modules/ # Route modules (chat, stream, trigger) -│ ├── middlewares/ # CORS, error handling, bearer auth -│ ├── utils/ # SSE utilities -│ └── models.ts # Zod request schemas +├── apps/ # Application services +│ └── agent/ # Agent Gateway (Bun/Elysia) +│ └── src/ +│ ├── index.ts # Elysia server entry point +│ ├── modules/ # Route modules (chat, stream, trigger) +│ ├── middlewares/ # CORS, error handling, bearer auth +│ ├── utils/ # SSE utilities +│ └── models.ts # Zod request schemas ├── packages/ # TypeScript monorepo │ ├── agent/ # Core agent library (@memoh/agent) │ │ └── src/ @@ -213,7 +214,7 @@ Migrations live in `db/migrations/` and follow a dual-update convention: ### Agent Development - The core agent logic lives in `packages/agent/` (`@memoh/agent`), providing reusable agent streaming, tool execution, and prompt management. -- The Agent Gateway (`agent/`) is a thin Elysia HTTP service that uses `@memoh/agent` for processing. +- The Agent Gateway (`apps/agent/`) is a thin Elysia HTTP service that uses `@memoh/agent` for processing. - AI model providers (Anthropic, OpenAI, Google) are integrated via Vercel AI SDK. - Tools (MCP, web search, subagent, skill) are defined in `packages/agent/src/tools/`. - Prompt templates (system, heartbeat, schedule, subagent) are in `packages/agent/src/prompts/`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e24776d..539b41b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,7 +64,8 @@ devenv/ — Dev environment (docker-compose, dev Dockerfiles, app.dev.toml, docker/ — Production Docker build & runtime (Dockerfiles, entrypoints) cmd/ — Go application entry points internal/ — Go backend core code -agent/ — Agent Gateway (Bun/Elysia) +apps/ — Application services (Agent Gateway, etc.) + agent/ — Agent Gateway (Bun/Elysia) packages/ — Frontend monorepo (web, ui, sdk, cli, config) db/ — Database migrations and queries scripts/ — Utility scripts diff --git a/agent/.gitignore b/apps/agent/.gitignore similarity index 100% rename from agent/.gitignore rename to apps/agent/.gitignore diff --git a/agent/README.md b/apps/agent/README.md similarity index 100% rename from agent/README.md rename to apps/agent/README.md diff --git a/agent/bun.lock b/apps/agent/bun.lock similarity index 100% rename from agent/bun.lock rename to apps/agent/bun.lock diff --git a/agent/mise.toml b/apps/agent/mise.toml similarity index 100% rename from agent/mise.toml rename to apps/agent/mise.toml diff --git a/agent/package.json b/apps/agent/package.json similarity index 100% rename from agent/package.json rename to apps/agent/package.json diff --git a/agent/src/index.ts b/apps/agent/src/index.ts similarity index 98% rename from agent/src/index.ts rename to apps/agent/src/index.ts index d89fc2be..3fb1e325 100644 --- a/agent/src/index.ts +++ b/apps/agent/src/index.ts @@ -6,7 +6,7 @@ import { loadConfig, getBaseUrl as getBaseUrlByConfig } from '@memoh/config' import { AgentAuthContext, AuthFetcher } from '@memoh/agent' const configuredPath = process.env.MEMOH_CONFIG_PATH?.trim() || process.env.CONFIG_PATH?.trim() -const configPath = configuredPath && configuredPath.length > 0 ? configuredPath : '../config.toml' +const configPath = configuredPath && configuredPath.length > 0 ? configuredPath : '../../config.toml' const config = loadConfig(configPath) export const getBaseUrl = () => { diff --git a/agent/src/middlewares/bearer.ts b/apps/agent/src/middlewares/bearer.ts similarity index 100% rename from agent/src/middlewares/bearer.ts rename to apps/agent/src/middlewares/bearer.ts diff --git a/agent/src/middlewares/cors.ts b/apps/agent/src/middlewares/cors.ts similarity index 100% rename from agent/src/middlewares/cors.ts rename to apps/agent/src/middlewares/cors.ts diff --git a/agent/src/middlewares/error.ts b/apps/agent/src/middlewares/error.ts similarity index 100% rename from agent/src/middlewares/error.ts rename to apps/agent/src/middlewares/error.ts diff --git a/agent/src/models.ts b/apps/agent/src/models.ts similarity index 100% rename from agent/src/models.ts rename to apps/agent/src/models.ts diff --git a/agent/src/modules/chat.ts b/apps/agent/src/modules/chat.ts similarity index 100% rename from agent/src/modules/chat.ts rename to apps/agent/src/modules/chat.ts diff --git a/agent/src/utils/sse.ts b/apps/agent/src/utils/sse.ts similarity index 100% rename from agent/src/utils/sse.ts rename to apps/agent/src/utils/sse.ts diff --git a/agent/tsconfig.json b/apps/agent/tsconfig.json similarity index 100% rename from agent/tsconfig.json rename to apps/agent/tsconfig.json diff --git a/devenv/docker-compose.yml b/devenv/docker-compose.yml index 483a8910..f895c2f0 100644 --- a/devenv/docker-compose.yml +++ b/devenv/docker-compose.yml @@ -104,7 +104,7 @@ services: agent: image: oven/bun:1-alpine container_name: memoh-dev-agent - working_dir: /workspace/agent + working_dir: /workspace/apps/agent command: ["bun", "run", "--watch", "src/index.ts"] volumes: - ..:/workspace diff --git a/docker/Dockerfile.agent b/docker/Dockerfile.agent index f9f513e2..cf1fd9cd 100644 --- a/docker/Dockerfile.agent +++ b/docker/Dockerfile.agent @@ -4,21 +4,21 @@ FROM --platform=$BUILDPLATFORM oven/bun:1 AS builder WORKDIR /build # Set up workspace structure so bun can resolve workspace deps (@memoh/config, @memoh/agent) -COPY agent/package.json agent/bun.lock* ./agent/ +COPY apps/agent/package.json apps/agent/bun.lock* ./apps/agent/ COPY packages/config/package.json ./packages/config/package.json COPY packages/agent/package.json ./packages/agent/package.json # Create root package.json with workspace config -RUN echo '{"name":"@memoh/monorepo","private":true,"workspaces":["agent","packages/*"]}' > package.json +RUN echo '{"name":"@memoh/monorepo","private":true,"workspaces":["apps/*","packages/*"]}' > package.json -RUN cd agent && bun install +RUN cd apps/agent && bun install # Copy source files COPY packages/config/ ./packages/config/ COPY packages/agent/ ./packages/agent/ -COPY agent/ ./agent/ +COPY apps/agent/ ./apps/agent/ -RUN cd agent && bun run build +RUN cd apps/agent && bun run build FROM oven/bun:1-alpine @@ -26,9 +26,9 @@ WORKDIR /app RUN apk add --no-cache ca-certificates wget -COPY --from=builder /build/agent/dist /app/dist -COPY --from=builder /build/agent/node_modules /app/node_modules -COPY --from=builder /build/agent/package.json /app/package.json +COPY --from=builder /build/apps/agent/dist /app/dist +COPY --from=builder /build/apps/agent/node_modules /app/node_modules +COPY --from=builder /build/apps/agent/package.json /app/package.json COPY --from=builder /build/node_modules /node_modules EXPOSE 8081 diff --git a/eslint.config.mjs b/eslint.config.mjs index c649d8cb..78ce62ef 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -8,7 +8,7 @@ export default [ ...vue.configs['flat/recommended'], { ignores: ['**/node_modules/**', '**/dist/**', '**/cache/**', 'packages/sdk/src/**'] }, { - files: ['packages/**/*.{js,jsx,ts,tsx}', 'agent/**/*.{js,jsx,ts,tsx}'], + files: ['packages/**/*.{js,jsx,ts,tsx}', 'apps/**/*.{js,jsx,ts,tsx}'], languageOptions: { parserOptions: { ecmaVersion: 2022, @@ -22,7 +22,7 @@ export default [ }, }, { - files: ['packages/**/*.vue', 'agent/**/*.vue'], + files: ['packages/**/*.vue', 'apps/**/*.vue'], languageOptions: { parser: vueParser, parserOptions: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccd91488..0153e0f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,23 +46,23 @@ importers: specifier: ^10.2.0 version: 10.2.0(eslint@9.39.2(jiti@2.6.1)) - agent: + apps/agent: dependencies: '@elysiajs/bearer': specifier: ^1.4.2 - version: 1.4.2(elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)) + version: 1.4.2(elysia@1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)) '@elysiajs/cors': specifier: ^1.4.1 - version: 1.4.1(elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)) + version: 1.4.1(elysia@1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)) '@memoh/agent': specifier: workspace:* - version: link:../packages/agent + version: link:../../packages/agent '@memoh/config': specifier: workspace:* - version: link:../packages/config + version: link:../../packages/config '@modelcontextprotocol/sdk': specifier: ^1.25.2 - version: 1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.4)(zod@4.3.5) + version: 1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.4)(zod@4.3.6) '@mozilla/readability': specifier: ^0.6.0 version: 0.6.0 @@ -71,10 +71,10 @@ importers: version: 5.0.6 ai: specifier: ^6.0.25 - version: 6.0.25(zod@4.3.5) + version: 6.0.25(zod@4.3.6) elysia: specifier: latest - version: 1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3) + version: 1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3) toml: specifier: ^3.0.0 version: 3.0.0 @@ -83,11 +83,11 @@ importers: version: 7.2.2 zod: specifier: ^4.3.5 - version: 4.3.5 + version: 4.3.6 devDependencies: bun-types: specifier: latest - version: 1.3.9 + version: 1.3.10 docs: devDependencies: @@ -2686,6 +2686,9 @@ packages: engines: {node: '>=18'} hasBin: true + bun-types@1.3.10: + resolution: {integrity: sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg==} + bun-types@1.3.9: resolution: {integrity: sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==} @@ -3175,8 +3178,8 @@ packages: electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} - elysia@1.4.25: - resolution: {integrity: sha512-liKjavH99Gpzrv9cDil6uYWmPuqESfPFV1FIaFSd3iNqo3y7e29sN43VxFIK8tWWnyi6eDAmi2SZk8hNAMQMyg==} + elysia@1.4.27: + resolution: {integrity: sha512-2UlmNEjPJVA/WZVPYKy+KdsrfFwwNlqSBW1lHz6i2AHc75k7gV4Rhm01kFeotH7PDiHIX2G8X3KnRPc33SGVIg==} peerDependencies: '@sinclair/typebox': '>= 0.34.0 < 1' '@types/bun': '>= 1.2.0' @@ -5273,13 +5276,6 @@ snapshots: '@ai-sdk/provider-utils': 4.0.4(zod@4.3.6) zod: 4.3.6 - '@ai-sdk/gateway@3.0.10(zod@4.3.5)': - dependencies: - '@ai-sdk/provider': 3.0.2 - '@ai-sdk/provider-utils': 4.0.4(zod@4.3.5) - '@vercel/oidc': 3.1.0 - zod: 4.3.5 - '@ai-sdk/gateway@3.0.10(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 @@ -5319,13 +5315,6 @@ snapshots: eventsource-parser: 3.0.6 zod: 4.3.6 - '@ai-sdk/provider-utils@4.0.4(zod@4.3.5)': - dependencies: - '@ai-sdk/provider': 3.0.2 - '@standard-schema/spec': 1.1.0 - eventsource-parser: 3.0.6 - zod: 4.3.5 - '@ai-sdk/provider-utils@4.0.4(zod@4.3.6)': dependencies: '@ai-sdk/provider': 3.0.2 @@ -5755,13 +5744,13 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} - '@elysiajs/bearer@1.4.2(elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))': + '@elysiajs/bearer@1.4.2(elysia@1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))': dependencies: - elysia: 1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3) + elysia: 1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3) - '@elysiajs/cors@1.4.1(elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))': + '@elysiajs/cors@1.4.1(elysia@1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))': dependencies: - elysia: 1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3) + elysia: 1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3) '@esbuild-kit/core-utils@3.3.2': dependencies: @@ -6473,7 +6462,7 @@ snapshots: '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/sdk@1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.4)(zod@4.3.5)': + '@modelcontextprotocol/sdk@1.25.2(@cfworker/json-schema@4.1.1)(hono@4.11.4)(zod@4.3.6)': dependencies: '@hono/node-server': 1.19.9(hono@4.11.4) ajv: 8.17.1 @@ -6489,8 +6478,8 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.3.5 - zod-to-json-schema: 3.25.1(zod@4.3.5) + zod: 4.3.6 + zod-to-json-schema: 3.25.1(zod@4.3.6) optionalDependencies: '@cfworker/json-schema': 4.1.1 transitivePeerDependencies: @@ -7409,14 +7398,6 @@ snapshots: agent-base@7.1.4: optional: true - ai@6.0.25(zod@4.3.5): - dependencies: - '@ai-sdk/gateway': 3.0.10(zod@4.3.5) - '@ai-sdk/provider': 3.0.2 - '@ai-sdk/provider-utils': 4.0.4(zod@4.3.5) - '@opentelemetry/api': 1.9.0 - zod: 4.3.5 - ai@6.0.25(zod@4.3.6): dependencies: '@ai-sdk/gateway': 3.0.10(zod@4.3.6) @@ -7592,6 +7573,10 @@ snapshots: transitivePeerDependencies: - magicast + bun-types@1.3.10: + dependencies: + '@types/node': 24.10.4 + bun-types@1.3.9: dependencies: '@types/node': 24.10.4 @@ -8078,7 +8063,7 @@ snapshots: electron-to-chromium@1.5.267: {} - elysia@1.4.25(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3): + elysia@1.4.27(@sinclair/typebox@0.34.47)(@types/bun@1.3.9)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3): dependencies: '@sinclair/typebox': 0.34.47 cookie: 1.1.1 @@ -10240,9 +10225,9 @@ snapshots: yoctocolors-cjs@2.1.3: {} - zod-to-json-schema@3.25.1(zod@4.3.5): + zod-to-json-schema@3.25.1(zod@4.3.6): dependencies: - zod: 4.3.5 + zod: 4.3.6 zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index df86debd..a148eb85 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,7 @@ packages: - 'packages/*' + - 'apps/*' - 'docs' - - 'agent' onlyBuiltDependencies: - sqlite3 \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh index 42450064..0d43446b 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -133,7 +133,7 @@ prepare_assets() { patch_jsdom_style_loader_for_compile trap 'restore_jsdom_style_loader_patch' RETURN ( - cd "$ROOT_DIR/agent" + cd "$ROOT_DIR/apps/agent" bun build src/index.ts --compile --target "$bun_compile_target" --outfile "$AGENT_DIR/$agent_bin_name" ) restore_jsdom_style_loader_patch @@ -151,9 +151,9 @@ JSDOM_XHR_IMPL_BACKUP="" patch_jsdom_style_loader_for_compile() { local css_path css_json - JSDOM_STYLE_RULES_FILE="$(node -e "try{process.stdout.write(require.resolve('jsdom/lib/jsdom/living/helpers/style-rules.js',{paths:['$ROOT_DIR/agent']}))}catch{process.exit(1)}" 2>/dev/null || true)" - css_path="$(node -e "try{process.stdout.write(require.resolve('jsdom/lib/jsdom/browser/default-stylesheet.css',{paths:['$ROOT_DIR/agent']}))}catch{process.exit(1)}" 2>/dev/null || true)" - JSDOM_XHR_IMPL_FILE="$(node -e "try{process.stdout.write(require.resolve('jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js',{paths:['$ROOT_DIR/agent']}))}catch{process.exit(1)}" 2>/dev/null || true)" + JSDOM_STYLE_RULES_FILE="$(node -e "try{process.stdout.write(require.resolve('jsdom/lib/jsdom/living/helpers/style-rules.js',{paths:['$ROOT_DIR/apps/agent']}))}catch{process.exit(1)}" 2>/dev/null || true)" + css_path="$(node -e "try{process.stdout.write(require.resolve('jsdom/lib/jsdom/browser/default-stylesheet.css',{paths:['$ROOT_DIR/apps/agent']}))}catch{process.exit(1)}" 2>/dev/null || true)" + JSDOM_XHR_IMPL_FILE="$(node -e "try{process.stdout.write(require.resolve('jsdom/lib/jsdom/living/xhr/XMLHttpRequest-impl.js',{paths:['$ROOT_DIR/apps/agent']}))}catch{process.exit(1)}" 2>/dev/null || true)" if [[ -z "$JSDOM_STYLE_RULES_FILE" || -z "$css_path" || -z "$JSDOM_XHR_IMPL_FILE" ]]; then log "skip jsdom patch (jsdom sources not resolved)" diff --git a/tsconfig.json b/tsconfig.json index 9ef6cc97..ef87cde9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,6 @@ "skipDefaultLibCheck": true, "skipLibCheck": true, }, - "include": ["packages/**/*.ts", "packages/**/*/src/*", "packages/**/*/src/**/*"], + "include": ["packages/**/*.ts", "packages/**/*/src/*", "packages/**/*/src/**/*", "apps/**/*.ts", "apps/**/*/src/*", "apps/**/*/src/**/*"], "exclude": ["node_modules", "dist"] } \ No newline at end of file