Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
"dependencies": {
"@kernlang/agon-engines": "^0.1.1",
"@kernlang/agon-dedup": "^0.1.6",
"@kernlang/agon-mcp": "^0.1.6",
"@ai-sdk/anthropic": "^3.0.67",
"@ai-sdk/openai-compatible": "^2.0.40",
"@kernlang/protocol": "~3.5.8",
Expand Down
12 changes: 9 additions & 3 deletions packages/cli/src/generated/cesar/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -999,14 +999,20 @@ export function mcpConfigFingerprint(config: any): string {
}

/**
* Resolve the agon-orchestration MCP server entry (packages/mcp/dist/index.js). The CLI ships as a tsup BUNDLE (chunks at packages/cli/dist/), so the old fixed '../../../../mcp/dist/index.js' relative to import.meta.url resolved to a NONEXISTENT path — the MCP server never spawned and Cesar saw zero tools (slow scraping fallback). Resolution order: (1) node module resolution of @kernlang/agon-mcp (works installed AND monorepo-via-symlink), (2) walk up to the repo root containing packages/mcp/dist/index.js (monorepo without a symlink), (3) the original relative guess as a last resort. `fromUrl` is for tests; defaults to this module's URL.
* Resolve the agon-orchestration MCP server entry. The CLI ships as a tsup BUNDLE that ALSO emits the MCP server to <cli-dist>/mcp/index.js (see tsup.config.ts), so the published install is self-contained — no @kernlang/agon-mcp npm dependency. Resolution order: (0) the bundled sibling <cli-dist>/mcp/index.js (the published, self-contained path), (1) node module resolution of @kernlang/agon-mcp (monorepo-via-symlink / legacy installs), (2) walk up to the repo root containing packages/mcp/dist/index.js (monorepo without a symlink), (3) the original relative guess as a last resort. `fromUrl` is for tests; defaults to this module's URL.
*/
// @kern-source: session:961
export function resolveAgonMcpServerPath(fromUrl?: string): string {
const raw = fromUrl ?? import.meta.url;
// Accept either a file: URL (normal) or a bare path (defensive): fileURLToPath
// throws ERR_INVALID_URL_SCHEME on a bare path, so normalise first.
const url = raw.startsWith('file:') ? raw : pathToFileURL(raw).href;
// 0) Bundled sibling — cli's tsup build emits the MCP server to
// <cli-dist>/mcp/index.js (see tsup.config.ts). This is the PUBLISHED
// path: self-contained, no @kernlang/agon-mcp dependency. The importing
// chunk lives in the cli dist dir, so dist/mcp/index.js sits beside it.
const bundledSibling = join(dirname(fileURLToPath(url)), 'mcp', 'index.js');
if (existsSync(bundledSibling)) return bundledSibling;
// 1) Installed/published OR monorepo workspace symlink — node resolution.
try {
const req = createRequire(url);
Expand All @@ -1029,7 +1035,7 @@ export function resolveAgonMcpServerPath(fromUrl?: string): string {
/**
* Single source of truth for which backend a Cesar engine will actually use. Honours config.cesarBackend preference ('auto' | 'cli' | 'api'). Pure — no side effects beyond registry lookups. Returns backend='none' when the engine has neither a usable binary nor an API key; callers decide how to handle that.
*/
// @kern-source: session:987
// @kern-source: session:993
export function resolveCesarBackend(ctx: HandlerContext, engineId?: string): { backend: 'cli'|'api'|'none', binaryPath: string, hasBinary: boolean, hasApi: boolean, engine: any } {
const config = ctx.config;
const cesarEngineId = engineId ?? (config as any).cesarEngine ?? config.forgeFixedStarter ?? 'claude';
Expand All @@ -1054,7 +1060,7 @@ export function resolveCesarBackend(ctx: HandlerContext, engineId?: string): { b
return { backend: 'none', binaryPath: '', hasBinary, hasApi, engine };
}

// @kern-source: session:1013
// @kern-source: session:1019
export async function ensureCesarSession(ctx: HandlerContext): Promise<PersistentSession> {
const config = ctx.config;
const cesarEngineId = (config as any).cesarEngine ?? config.forgeFixedStarter ?? 'claude';
Expand Down
8 changes: 7 additions & 1 deletion packages/cli/src/kern/cesar/session.kern
Original file line number Diff line number Diff line change
Expand Up @@ -959,12 +959,18 @@ fn name=mcpConfigFingerprint params="config:any" returns=string
>>>

fn name=resolveAgonMcpServerPath params="fromUrl?:string" returns=string export=true
doc "Resolve the agon-orchestration MCP server entry (packages/mcp/dist/index.js). The CLI ships as a tsup BUNDLE (chunks at packages/cli/dist/), so the old fixed '../../../../mcp/dist/index.js' relative to import.meta.url resolved to a NONEXISTENT path — the MCP server never spawned and Cesar saw zero tools (slow scraping fallback). Resolution order: (1) node module resolution of @kernlang/agon-mcp (works installed AND monorepo-via-symlink), (2) walk up to the repo root containing packages/mcp/dist/index.js (monorepo without a symlink), (3) the original relative guess as a last resort. `fromUrl` is for tests; defaults to this module's URL."
doc "Resolve the agon-orchestration MCP server entry. The CLI ships as a tsup BUNDLE that ALSO emits the MCP server to <cli-dist>/mcp/index.js (see tsup.config.ts), so the published install is self-contained — no @kernlang/agon-mcp npm dependency. Resolution order: (0) the bundled sibling <cli-dist>/mcp/index.js (the published, self-contained path), (1) node module resolution of @kernlang/agon-mcp (monorepo-via-symlink / legacy installs), (2) walk up to the repo root containing packages/mcp/dist/index.js (monorepo without a symlink), (3) the original relative guess as a last resort. `fromUrl` is for tests; defaults to this module's URL."
handler <<<
const raw = fromUrl ?? import.meta.url;
// Accept either a file: URL (normal) or a bare path (defensive): fileURLToPath
// throws ERR_INVALID_URL_SCHEME on a bare path, so normalise first.
const url = raw.startsWith('file:') ? raw : pathToFileURL(raw).href;
// 0) Bundled sibling — cli's tsup build emits the MCP server to
// <cli-dist>/mcp/index.js (see tsup.config.ts). This is the PUBLISHED
// path: self-contained, no @kernlang/agon-mcp dependency. The importing
// chunk lives in the cli dist dir, so dist/mcp/index.js sits beside it.
const bundledSibling = join(dirname(fileURLToPath(url)), 'mcp', 'index.js');
if (existsSync(bundledSibling)) return bundledSibling;
// 1) Installed/published OR monorepo workspace symlink — node resolution.
try {
const req = createRequire(url);
Expand Down
12 changes: 11 additions & 1 deletion packages/cli/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ function copyEngines(outDir: string) {
}

export default defineConfig({
entry: ['src/index.ts'],
// Two entries from ONE build: the CLI itself, and the agon-orchestration MCP
// server (../mcp/src/index.ts) emitted to dist/mcp/index.js. Bundling the MCP
// server in keeps `npm i -g @kernlang/agon` self-contained — Cesar spawns the
// bundled copy (see resolveAgonMcpServerPath) instead of an unpublished
// @kernlang/agon-mcp dependency. Both entries inline agon-core and share chunks.
entry: { index: 'src/index.ts', 'mcp/index': '../mcp/src/index.ts' },
format: ['esm'],
dts: false,
sourcemap: true,
Expand Down Expand Up @@ -48,5 +53,10 @@ export default defineConfig({
},
async onSuccess() {
copyEngines(join(process.cwd(), 'dist'));
// The bundled MCP server resolves engines via <its-own-dir>/engines
// (resolveBuiltinEnginesDir), so it needs a copy next to dist/mcp/index.js —
// otherwise the spawned server starts with ZERO engines and Cesar silently
// loses its orchestration tools.
copyEngines(join(process.cwd(), 'dist', 'mcp'));
},
});
4 changes: 1 addition & 3 deletions packages/mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
{
"name": "@kernlang/agon-mcp",
"version": "0.1.6",
"private": true,
"type": "module",
"bin": {
"agon-mcp": "./dist/index.js"
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
Expand Down
7 changes: 6 additions & 1 deletion packages/mcp/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#!/usr/bin/env node
// NOTE: no `#!/usr/bin/env node` shebang here on purpose. This entry is bundled
// into the cli package (cli/dist/mcp/index.js) by cli's tsup, whose `banner`
// already prepends the shebang — a second one here lands on line 2 of the bundle
// and is a hard SyntaxError. The server is always spawned via `node <path>`
// (see resolveAgonMcpServerPath / the agon-orchestration mcpServer config), so it
// never needs to be directly executable.
// Agon Orchestration MCP Server — entry point
// Spawned by companion engines (Codex, Gemini, OpenCode) to expose
// Tribunal, Brainstorm, Campfire, Forge, etc. as MCP tools.
Expand Down